Merge branch 'master' into loadout-loading
This commit is contained in:
@@ -206,7 +206,7 @@
|
||||
for(var/datum/ntnet_conversation/chan in chat_channels)
|
||||
if(chan.id == id)
|
||||
return chan
|
||||
|
||||
|
||||
// Resets the IDS alarm
|
||||
/datum/ntnet/proc/resetIDS()
|
||||
intrusion_detection_alarm = FALSE
|
||||
|
||||
+132
-101
@@ -1,12 +1,12 @@
|
||||
|
||||
////////////////////////////////
|
||||
/proc/message_admins(msg)
|
||||
msg = "<span class=\"admin\"><span class=\"prefix\">ADMIN LOG:</span> <span class=\"message linkify\">[msg]</span></span>"
|
||||
to_chat(GLOB.admins, msg)
|
||||
msg = "<span class=\"admin filter_adminlog\"><span class=\"prefix\">ADMIN LOG:</span> <span class=\"message linkify\">[msg]</span></span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
|
||||
/proc/relay_msg_admins(msg)
|
||||
msg = "<span class=\"admin\"><span class=\"prefix\">RELAY:</span> <span class=\"message linkify\">[msg]</span></span>"
|
||||
to_chat(GLOB.admins, msg)
|
||||
msg = "<span class=\"admin filter_adminlog\"><span class=\"prefix\">RELAY:</span> <span class=\"message linkify\">[msg]</span></span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////Panels
|
||||
@@ -22,7 +22,7 @@
|
||||
log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].")
|
||||
|
||||
if(!M)
|
||||
to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.")
|
||||
to_chat(usr, "<span class='warning'>You seem to be selecting a mob that doesn't exist anymore.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/body = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Options for [M.key]</title></head>"
|
||||
@@ -65,6 +65,7 @@
|
||||
body += "<a href='?_src_=vars;[HrefToken()];Vars=[REF(M)]'>VV</a> - "
|
||||
if(M.mind)
|
||||
body += "<a href='?_src_=holder;[HrefToken()];traitor=[REF(M)]'>TP</a> - "
|
||||
// body += "<a href='?_src_=holder;[HrefToken()];skill=[REF(M)]'>SKILLS</a> - "
|
||||
else
|
||||
body += "<a href='?_src_=holder;[HrefToken()];initmind=[REF(M)]'>Init Mind</a> - "
|
||||
if (iscyborg(M))
|
||||
@@ -122,6 +123,7 @@
|
||||
body += "<A href='?_src_=holder;[HrefToken()];traitor=[REF(M)]'>Traitor panel</A> | "
|
||||
body += "<A href='?_src_=holder;[HrefToken()];narrateto=[REF(M)]'>Narrate to</A> | "
|
||||
body += "<A href='?_src_=holder;[HrefToken()];subtlemessage=[REF(M)]'>Subtle message</A> | "
|
||||
// body += "<A href='?_src_=holder;[HrefToken()];playsoundto=[REF(M)]'>Play sound to</A> | "
|
||||
body += "<A href='?_src_=holder;[HrefToken()];languagemenu=[REF(M)]'>Language Menu</A>"
|
||||
|
||||
if (M.client)
|
||||
@@ -216,7 +218,7 @@
|
||||
if (!istype(src, /datum/admins))
|
||||
src = usr.client.holder
|
||||
if (!istype(src, /datum/admins))
|
||||
to_chat(usr, "Error: you are not an admin!")
|
||||
to_chat(usr, "Error: you are not an admin!", confidential = TRUE)
|
||||
return
|
||||
var/dat
|
||||
dat = text("<HEAD><TITLE>Admin Newscaster</TITLE></HEAD><H3>Admin Newscaster Unit</H3>")
|
||||
@@ -242,7 +244,7 @@
|
||||
dat+="<BR><HR><A href='?src=[REF(src)];[HrefToken()];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(GLOB.news_network.network_channels) )
|
||||
if( !length(GLOB.news_network.network_channels) )
|
||||
dat+="<I>No active channels found...</I>"
|
||||
else
|
||||
for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
|
||||
@@ -295,7 +297,7 @@
|
||||
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) )
|
||||
if( !length(src.admincaster_feed_channel.messages) )
|
||||
dat+="<I>No feed messages found in channel...</I><BR>"
|
||||
else
|
||||
var/i = 0
|
||||
@@ -317,7 +319,7 @@
|
||||
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(GLOB.news_network.network_channels))
|
||||
if(!length(GLOB.news_network.network_channels))
|
||||
dat+="<I>No feed channels found active...</I><BR>"
|
||||
else
|
||||
for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
|
||||
@@ -328,7 +330,7 @@
|
||||
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(GLOB.news_network.network_channels))
|
||||
if(!length(GLOB.news_network.network_channels))
|
||||
dat+="<I>No feed channels found active...</I><BR>"
|
||||
else
|
||||
for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
|
||||
@@ -339,7 +341,7 @@
|
||||
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)];[HrefToken()];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) )
|
||||
if( !length(src.admincaster_feed_channel.messages) )
|
||||
dat+="<I>No feed messages found in channel...</I><BR>"
|
||||
else
|
||||
for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
|
||||
@@ -356,7 +358,7 @@
|
||||
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) )
|
||||
if( !length(src.admincaster_feed_channel.messages) )
|
||||
dat+="<I>No feed messages found in channel...</I><BR>"
|
||||
else
|
||||
for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
|
||||
@@ -426,7 +428,6 @@
|
||||
"}
|
||||
if(GLOB.master_mode == "secret")
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_secret=1'>(Force Secret Mode)</A><br>"
|
||||
|
||||
if(GLOB.master_mode == "dynamic")
|
||||
if(SSticker.current_state <= GAME_STATE_PREGAME)
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart=1'>(Force Roundstart Rulesets)</A><br>"
|
||||
@@ -449,7 +450,6 @@
|
||||
dat += "<hr/>"
|
||||
if(SSticker.IsRoundInProgress())
|
||||
dat += "<a href='?src=[REF(src)];[HrefToken()];gamemode_panel=1'>(Game Mode Panel)</a><BR>"
|
||||
|
||||
dat += {"
|
||||
<BR>
|
||||
<A href='?src=[REF(src)];[HrefToken()];create_object=1'>Create Object</A><br>
|
||||
@@ -461,7 +461,7 @@
|
||||
if(marked_datum && istype(marked_datum, /atom))
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];dupe_marked_datum=1'>Duplicate Marked Datum</A><br>"
|
||||
|
||||
usr << browse(dat, "window=admin2;size=210x200")
|
||||
usr << browse(dat, "window=admin2;size=240x280")
|
||||
return
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge
|
||||
@@ -475,33 +475,42 @@
|
||||
if (!usr.client.holder)
|
||||
return
|
||||
|
||||
var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
|
||||
var/localhost_addresses = list("127.0.0.1", "::1")
|
||||
var/list/options = list("Regular Restart", "Regular Restart (with delay)", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
|
||||
if(world.TgsAvailable())
|
||||
options += "Server Restart (Kill and restart DD)";
|
||||
|
||||
var/rebootconfirm
|
||||
if(SSticker.admin_delay_notice)
|
||||
if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") == "Yes")
|
||||
rebootconfirm = TRUE
|
||||
else
|
||||
rebootconfirm = TRUE
|
||||
if(rebootconfirm)
|
||||
var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
|
||||
if(result)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
|
||||
switch(result)
|
||||
if("Regular Restart")
|
||||
SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
|
||||
if("Hard Restart (No Delay, No Feeback Reason)")
|
||||
to_chat(world, "World reboot - [init_by]")
|
||||
world.Reboot()
|
||||
if("Hardest Restart (No actions, just reboot)")
|
||||
to_chat(world, "Hard world reboot - [init_by]")
|
||||
world.Reboot(fast_track = TRUE)
|
||||
if("Server Restart (Kill and restart DD)")
|
||||
to_chat(world, "Server restart - [init_by]")
|
||||
world.TgsEndProcess()
|
||||
if(alert(usr, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") != "Yes")
|
||||
return FALSE
|
||||
|
||||
var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
|
||||
if(result)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
|
||||
switch(result)
|
||||
if("Regular Restart")
|
||||
if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses)))
|
||||
if(alert("Are you sure you want to restart the server?","This server is live","Restart","Cancel") != "Restart")
|
||||
return FALSE
|
||||
SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
|
||||
if("Regular Restart (with delay)")
|
||||
var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null
|
||||
if(!delay)
|
||||
return FALSE
|
||||
if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses)))
|
||||
if(alert("Are you sure you want to restart the server?","This server is live","Restart","Cancel") != "Restart")
|
||||
return FALSE
|
||||
SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay * 10)
|
||||
if("Hard Restart (No Delay, No Feeback Reason)")
|
||||
to_chat(world, "World reboot - [init_by]")
|
||||
world.Reboot()
|
||||
if("Hardest Restart (No actions, just reboot)")
|
||||
to_chat(world, "Hard world reboot - [init_by]")
|
||||
world.Reboot(fast_track = TRUE)
|
||||
if("Server Restart (Kill and restart DD)")
|
||||
to_chat(world, "Server restart - [init_by]")
|
||||
world.TgsEndProcess()
|
||||
|
||||
/datum/admins/proc/end_round()
|
||||
set category = "Server"
|
||||
@@ -529,7 +538,7 @@
|
||||
if(message)
|
||||
if(!check_rights(R_SERVER,0))
|
||||
message = adminscrub(message,500)
|
||||
to_chat(world, "<span class='adminnotice'><b>[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:</b></span>\n \t [message]")
|
||||
to_chat(world, "<span class='adminnotice'><b>[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:</b></span>\n \t [message]", confidential = TRUE)
|
||||
log_admin("Announce: [key_name(usr)] : [message]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -551,7 +560,7 @@
|
||||
else
|
||||
message_admins("[key_name(usr)] set the admin notice.")
|
||||
log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]")
|
||||
to_chat(world, "<span class ='adminnotice'><b>Admin Notice:</b>\n \t [new_admin_notice]</span>")
|
||||
to_chat(world, "<span class='adminnotice'><b>Admin Notice:</b>\n \t [new_admin_notice]</span>", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
GLOB.admin_notice = new_admin_notice
|
||||
return
|
||||
@@ -598,20 +607,29 @@
|
||||
set desc="Start the round RIGHT NOW"
|
||||
set name="Start Now"
|
||||
if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP)
|
||||
SSticker.start_immediately = TRUE
|
||||
log_admin("[usr.key] has started the game.")
|
||||
var/msg = ""
|
||||
if(SSticker.current_state == GAME_STATE_STARTUP)
|
||||
msg = " (The server is still setting up, but the round will be \
|
||||
started as soon as possible.)"
|
||||
message_admins("<font color='blue'>\
|
||||
[usr.key] has started the game.[msg]</font>")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return 1
|
||||
if(!SSticker.start_immediately)
|
||||
var/localhost_addresses = list("127.0.0.1", "::1")
|
||||
if(!(isnull(usr.client.address) || (usr.client.address in localhost_addresses)))
|
||||
if(alert("Are you sure you want to start the round?","Start Now","Start Now","Cancel") != "Start Now")
|
||||
return FALSE
|
||||
SSticker.start_immediately = TRUE
|
||||
log_admin("[usr.key] has started the game.")
|
||||
var/msg = ""
|
||||
if(SSticker.current_state == GAME_STATE_STARTUP)
|
||||
msg = " (The server is still setting up, but the round will be \
|
||||
started as soon as possible.)"
|
||||
message_admins("<font color='blue'>[usr.key] has started the game.[msg]</font>")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return TRUE
|
||||
SSticker.start_immediately = FALSE
|
||||
SSticker.SetTimeLeft(1800)
|
||||
to_chat(world, "<b>The game will start in 180 seconds.</b>")
|
||||
SEND_SOUND(world, sound(get_announcer_sound("attention")))
|
||||
message_admins("<font color='blue'>[usr.key] has cancelled immediate game start. Game will start in 180 seconds.</font>")
|
||||
log_admin("[usr.key] has cancelled immediate game start.")
|
||||
else
|
||||
to_chat(usr, "<font color='red'>Error: Start Now: Game has already started.</font>")
|
||||
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
/datum/admins/proc/toggleenter()
|
||||
set category = "Server"
|
||||
@@ -619,9 +637,9 @@
|
||||
set name="Toggle Entering"
|
||||
GLOB.enter_allowed = !( GLOB.enter_allowed )
|
||||
if (!( GLOB.enter_allowed ))
|
||||
to_chat(world, "<B>New players may no longer enter the game.</B>")
|
||||
to_chat(world, "<B>New players may no longer enter the game.</B>", confidential = TRUE)
|
||||
else
|
||||
to_chat(world, "<B>New players may now enter the game.</B>")
|
||||
to_chat(world, "<B>New players may now enter the game.</B>", confidential = TRUE)
|
||||
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()
|
||||
@@ -634,9 +652,9 @@
|
||||
var/alai = CONFIG_GET(flag/allow_ai)
|
||||
CONFIG_SET(flag/allow_ai, !alai)
|
||||
if (alai)
|
||||
to_chat(world, "<B>The AI job is no longer chooseable.</B>")
|
||||
to_chat(world, "<B>The AI job is no longer chooseable.</B>", confidential = TRUE)
|
||||
else
|
||||
to_chat(world, "<B>The AI job is chooseable now.</B>")
|
||||
to_chat(world, "<B>The AI job is chooseable now.</B>", confidential = TRUE)
|
||||
log_admin("[key_name(usr)] toggled AI allowed.")
|
||||
world.update_status()
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -654,7 +672,7 @@
|
||||
aiPlayer.end_multicam()
|
||||
log_admin("[key_name(usr)] toggled AI multicam.")
|
||||
world.update_status()
|
||||
to_chat(GLOB.ai_list | GLOB.admins, "<B>The AI [almcam ? "no longer" : "now"] has multicam.</B>")
|
||||
to_chat(GLOB.ai_list | GLOB.admins, "<B>The AI [almcam ? "no longer" : "now"] has multicam.</B>", confidential = TRUE)
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Multicam", "[!almcam ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/datum/admins/proc/toggleaban()
|
||||
@@ -664,9 +682,9 @@
|
||||
var/new_nores = !CONFIG_GET(flag/norespawn)
|
||||
CONFIG_SET(flag/norespawn, new_nores)
|
||||
if (!new_nores)
|
||||
to_chat(world, "<B>You may now respawn.</B>")
|
||||
to_chat(world, "<B>You may now respawn.</B>", confidential = TRUE)
|
||||
else
|
||||
to_chat(world, "<B>You may no longer respawn :(</B>")
|
||||
to_chat(world, "<B>You may no longer respawn :(</B>", confidential = TRUE)
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled respawn to [!new_nores ? "On" : "Off"].</span>")
|
||||
log_admin("[key_name(usr)] toggled respawn to [!new_nores ? "On" : "Off"].")
|
||||
world.update_status()
|
||||
@@ -675,7 +693,7 @@
|
||||
/datum/admins/proc/delay()
|
||||
set category = "Server"
|
||||
set desc="Delay the game start"
|
||||
set name="Delay pre-game"
|
||||
set name="Delay Pre-Game"
|
||||
|
||||
var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(SSticker.GetTimeLeft()/10)) as num|null
|
||||
if(SSticker.current_state > GAME_STATE_PREGAME)
|
||||
@@ -683,11 +701,12 @@
|
||||
if(newtime)
|
||||
newtime = newtime*10
|
||||
SSticker.SetTimeLeft(newtime)
|
||||
SSticker.start_immediately = FALSE
|
||||
if(newtime < 0)
|
||||
to_chat(world, "<b>The game start has been delayed.</b>")
|
||||
to_chat(world, "<b>The game start has been delayed.</b>", confidential = TRUE)
|
||||
log_admin("[key_name(usr)] delayed the round start.")
|
||||
else
|
||||
to_chat(world, "<b>The game will start in [DisplayTimeText(newtime)].</b>")
|
||||
to_chat(world, "<b>The game will start in [DisplayTimeText(newtime)].</b>", confidential = TRUE)
|
||||
SEND_SOUND(world, sound(get_announcer_sound("attention")))
|
||||
log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -724,20 +743,28 @@
|
||||
set desc = "(atom path) Spawn an atom"
|
||||
set name = "Spawn"
|
||||
|
||||
if(!check_rights(R_SPAWN))
|
||||
if(!check_rights(R_SPAWN) || !object)
|
||||
return
|
||||
|
||||
var/list/preparsed = splittext(object,":")
|
||||
var/path = preparsed[1]
|
||||
var/amount = 1
|
||||
if(preparsed.len > 1)
|
||||
amount = clamp(text2num(preparsed[2]),1, 50) //50 at a time!
|
||||
|
||||
var/chosen = pick_closest_path(path)
|
||||
if(!chosen)
|
||||
return
|
||||
var/turf/T = get_turf(usr)
|
||||
|
||||
var/chosen = pick_closest_path(object)
|
||||
if(!chosen)
|
||||
return
|
||||
if(ispath(chosen, /turf))
|
||||
T.ChangeTurf(chosen)
|
||||
else
|
||||
var/atom/A = new chosen(T)
|
||||
A.flags_1 |= ADMIN_SPAWNED_1
|
||||
for(var/i in 1 to amount)
|
||||
var/atom/A = new chosen(T)
|
||||
A.flags_1 |= ADMIN_SPAWNED_1
|
||||
|
||||
log_admin("[key_name(usr)] spawned [chosen] at [AREACOORD(usr)]")
|
||||
log_admin("[key_name(usr)] spawned [amount] x [chosen] at [AREACOORD(usr)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/datum/admins/proc/podspawn_atom(object as text)
|
||||
@@ -782,20 +809,18 @@
|
||||
log_admin("[key_name(usr)] spawned cargo pack [chosen] at [AREACOORD(usr)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Cargo") //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 GLOB.mob_list)
|
||||
/datum/admins/proc/show_traitor_panel(mob/target_mob in GLOB.mob_list)
|
||||
set category = "Admin"
|
||||
set desc = "Edit mobs's memory and role"
|
||||
set name = "Show Traitor Panel"
|
||||
|
||||
if(!istype(M))
|
||||
to_chat(usr, "This can only be used on instances of type /mob")
|
||||
var/datum/mind/target_mind = target_mob.mind
|
||||
if(!target_mind)
|
||||
to_chat(usr, "This mob has no mind!", confidential = TRUE)
|
||||
return
|
||||
if(!M.mind)
|
||||
to_chat(usr, "This mob has no mind!")
|
||||
if(!istype(target_mob) && !istype(target_mind))
|
||||
to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE)
|
||||
return
|
||||
|
||||
M.mind.traitor_panel()
|
||||
target_mind.traitor_panel()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
@@ -805,9 +830,9 @@
|
||||
set name="Toggle tinted welding helmes"
|
||||
GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh )
|
||||
if (GLOB.tinted_weldhelh)
|
||||
to_chat(world, "<B>The tinted_weldhelh has been enabled!</B>")
|
||||
to_chat(world, "<B>The tinted_weldhelh has been enabled!</B>", confidential = TRUE)
|
||||
else
|
||||
to_chat(world, "<B>The tinted_weldhelh has been disabled!</B>")
|
||||
to_chat(world, "<B>The tinted_weldhelh has been disabled!</B>", confidential = TRUE)
|
||||
log_admin("[key_name(usr)] toggled tinted_weldhelh.")
|
||||
message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.")
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Tinted Welding Helmets", "[GLOB.tinted_weldhelh ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -819,9 +844,9 @@
|
||||
var/new_guest_ban = !CONFIG_GET(flag/guest_ban)
|
||||
CONFIG_SET(flag/guest_ban, new_guest_ban)
|
||||
if (new_guest_ban)
|
||||
to_chat(world, "<B>Guests may no longer enter the game.</B>")
|
||||
to_chat(world, "<B>Guests may no longer enter the game.</B>", confidential = TRUE)
|
||||
else
|
||||
to_chat(world, "<B>Guests may now enter the game.</B>")
|
||||
to_chat(world, "<B>Guests may now enter the game.</B>", confidential = TRUE)
|
||||
log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.</span>")
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -832,37 +857,37 @@
|
||||
var/mob/living/silicon/S = i
|
||||
ai_number++
|
||||
if(isAI(S))
|
||||
to_chat(usr, "<b>AI [key_name(S, usr)]'s laws:</b>")
|
||||
to_chat(usr, "<b>AI [key_name(S, usr)]'s laws:</b>", confidential = TRUE)
|
||||
else if(iscyborg(S))
|
||||
var/mob/living/silicon/robot/R = S
|
||||
to_chat(usr, "<b>CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:</b>")
|
||||
to_chat(usr, "<b>CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [key_name(R.connected_ai)])":"(Independent)"]: laws:</b>", confidential = TRUE)
|
||||
else if (ispAI(S))
|
||||
to_chat(usr, "<b>pAI [key_name(S, usr)]'s laws:</b>")
|
||||
to_chat(usr, "<b>pAI [key_name(S, usr)]'s laws:</b>", confidential = TRUE)
|
||||
else
|
||||
to_chat(usr, "<b>SOMETHING SILICON [key_name(S, usr)]'s laws:</b>")
|
||||
to_chat(usr, "<b>SOMETHING SILICON [key_name(S, usr)]'s laws:</b>", confidential = TRUE)
|
||||
|
||||
if (S.laws == null)
|
||||
to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.")
|
||||
to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.", confidential = TRUE)
|
||||
else
|
||||
S.laws.show_laws(usr)
|
||||
if(!ai_number)
|
||||
to_chat(usr, "<b>No AIs located</b>" )
|
||||
to_chat(usr, "<b>No AIs located</b>" , confidential = TRUE)
|
||||
|
||||
/datum/admins/proc/output_all_devil_info()
|
||||
var/devil_number = 0
|
||||
for(var/datum/mind/D in SSticker.mode.devils)
|
||||
devil_number++
|
||||
var/datum/antagonist/devil/devil = D.has_antag_datum(/datum/antagonist/devil)
|
||||
to_chat(usr, "Devil #[devil_number]:<br><br>" + devil.printdevilinfo())
|
||||
to_chat(usr, "Devil #[devil_number]:<br><br>" + devil.printdevilinfo(), confidential = TRUE)
|
||||
if(!devil_number)
|
||||
to_chat(usr, "<b>No Devils located</b>" )
|
||||
to_chat(usr, "<b>No Devils located</b>" , confidential = TRUE)
|
||||
|
||||
/datum/admins/proc/output_devil_info(mob/living/M)
|
||||
if(is_devil(M))
|
||||
var/datum/antagonist/devil/devil = M.mind.has_antag_datum(/datum/antagonist/devil)
|
||||
to_chat(usr, devil.printdevilinfo())
|
||||
to_chat(usr, devil.printdevilinfo(), confidential = TRUE)
|
||||
else
|
||||
to_chat(usr, "<b>[M] is not a devil.")
|
||||
to_chat(usr, "<b>[M] is not a devil.", confidential = TRUE)
|
||||
|
||||
/datum/admins/proc/manage_free_slots()
|
||||
if(!check_rights())
|
||||
@@ -965,21 +990,21 @@
|
||||
if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk
|
||||
continue
|
||||
if(message)
|
||||
to_chat(C, message)
|
||||
to_chat(C, message, confidential = TRUE)
|
||||
kicked_client_names.Add("[C.key]")
|
||||
qdel(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
|
||||
//returns TRUE to let the dragdrop code know we are trapping this event
|
||||
//returns FALSE if we don't plan to trap the event
|
||||
/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/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
|
||||
return FALSE
|
||||
|
||||
if (!frommob.ckey)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
var/question = ""
|
||||
if (tomob.ckey)
|
||||
@@ -988,12 +1013,18 @@
|
||||
|
||||
var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No")
|
||||
if (ask != "Yes")
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
tomob.ghostize(0)
|
||||
// Disassociates observer mind from the body mind
|
||||
if(tomob.client)
|
||||
tomob.ghostize(FALSE)
|
||||
else
|
||||
for(var/mob/dead/observer/ghost in GLOB.dead_mob_list)
|
||||
if(tomob.mind == ghost.mind)
|
||||
ghost.mind = null
|
||||
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [frommob.key] in control of [tomob.name].</span>")
|
||||
log_admin("[key_name(usr)] stuffed [frommob.key] into [tomob.name].")
|
||||
@@ -1002,7 +1033,7 @@
|
||||
tomob.ckey = frommob.ckey
|
||||
qdel(frommob)
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/client/proc/adminGreet(logout)
|
||||
if(SSticker.HasRoundStarted())
|
||||
|
||||
@@ -4,19 +4,39 @@
|
||||
var/F = file("[GLOB.log_directory]/[subject].html")
|
||||
WRITE_FILE(F, "<small>[TIME_STAMP("hh:mm:ss", FALSE)] [REF(src)] ([x],[y],[z])</small> || [src] [message]<br>")
|
||||
|
||||
/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RCD, INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES, INVESTIGATE_CRYOGENICS) )
|
||||
/client/proc/investigate_show()
|
||||
set name = "Investigate"
|
||||
set category = "Admin"
|
||||
if(!holder)
|
||||
return
|
||||
switch(subject)
|
||||
if("notes, memos, watchlist")
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
browse_messages()
|
||||
|
||||
var/list/investigates = list(INVESTIGATE_RCD, INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES, INVESTIGATE_CRYOGENICS)
|
||||
|
||||
var/list/logs_present = list("notes, memos, watchlist")
|
||||
var/list/logs_missing = list("---")
|
||||
|
||||
for(var/subject in investigates)
|
||||
var/temp_file = file("[GLOB.log_directory]/[subject].html")
|
||||
if(fexists(temp_file))
|
||||
logs_present += subject
|
||||
else
|
||||
var/F = file("[GLOB.log_directory]/[subject].html")
|
||||
if(!fexists(F))
|
||||
to_chat(src, "<span class='danger'>No [subject] logfile was found.</span>")
|
||||
return
|
||||
src << browse(F,"window=investigate[subject];size=800x300")
|
||||
logs_missing += "[subject] (empty)"
|
||||
|
||||
var/list/combined = sortList(logs_present) + sortList(logs_missing)
|
||||
|
||||
var/selected = input("Investigate what?", "Investigate") as null|anything in combined
|
||||
|
||||
if(!(selected in combined) || selected == "---")
|
||||
return
|
||||
|
||||
selected = replacetext(selected, " (empty)", "")
|
||||
|
||||
if(selected == "notes, memos, watchlist" && check_rights(R_ADMIN))
|
||||
browse_messages()
|
||||
return
|
||||
|
||||
var/F = file("[GLOB.log_directory]/[selected].html")
|
||||
if(!fexists(F))
|
||||
to_chat(src, "<span class='danger'>No [selected] logfile was found.</span>", confidential = TRUE)
|
||||
return
|
||||
src << browse(F,"window=investigate[selected];size=800x300")
|
||||
|
||||
@@ -11,7 +11,8 @@ GLOBAL_PROTECT(admin_verbs_default)
|
||||
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
|
||||
/client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
|
||||
/client/proc/toggleprayers,
|
||||
/client/proc/toggleadminhelpsound
|
||||
/client/proc/toggleadminhelpsound,
|
||||
/client/proc/debugstatpanel,
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
|
||||
GLOBAL_PROTECT(admin_verbs_admin)
|
||||
@@ -260,36 +261,36 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS
|
||||
|
||||
var/rights = holder.rank.rights
|
||||
verbs += GLOB.admin_verbs_default
|
||||
add_verb(src, GLOB.admin_verbs_default)
|
||||
if(rights & R_BUILDMODE)
|
||||
verbs += /client/proc/togglebuildmodeself
|
||||
add_verb(src, /client/proc/togglebuildmodeself)
|
||||
if(rights & R_ADMIN)
|
||||
verbs += GLOB.admin_verbs_admin
|
||||
add_verb(src, GLOB.admin_verbs_admin)
|
||||
if(rights & R_BAN)
|
||||
verbs += GLOB.admin_verbs_ban
|
||||
add_verb(src, GLOB.admin_verbs_ban)
|
||||
if(rights & R_FUN)
|
||||
verbs += GLOB.admin_verbs_fun
|
||||
add_verb(src, GLOB.admin_verbs_fun)
|
||||
if(rights & R_SERVER)
|
||||
verbs += GLOB.admin_verbs_server
|
||||
add_verb(src, GLOB.admin_verbs_server)
|
||||
if(rights & R_DEBUG)
|
||||
verbs += GLOB.admin_verbs_debug
|
||||
add_verb(src, GLOB.admin_verbs_debug)
|
||||
if(rights & R_POSSESS)
|
||||
verbs += GLOB.admin_verbs_possess
|
||||
add_verb(src, GLOB.admin_verbs_possess)
|
||||
if(rights & R_PERMISSIONS)
|
||||
verbs += GLOB.admin_verbs_permissions
|
||||
add_verb(src, GLOB.admin_verbs_permissions)
|
||||
if(rights & R_STEALTH)
|
||||
verbs += /client/proc/stealth
|
||||
add_verb(src, /client/proc/stealth)
|
||||
if(rights & R_ADMIN)
|
||||
verbs += GLOB.admin_verbs_poll
|
||||
add_verb(src, GLOB.admin_verbs_poll)
|
||||
if(rights & R_SOUNDS)
|
||||
verbs += GLOB.admin_verbs_sounds
|
||||
add_verb(src, GLOB.admin_verbs_sounds)
|
||||
if(CONFIG_GET(string/invoke_youtubedl))
|
||||
verbs += /client/proc/play_web_sound
|
||||
add_verb(src, /client/proc/play_web_sound)
|
||||
if(rights & R_SPAWN)
|
||||
verbs += GLOB.admin_verbs_spawn
|
||||
add_verb(src, GLOB.admin_verbs_spawn)
|
||||
|
||||
/client/proc/remove_admin_verbs()
|
||||
verbs.Remove(
|
||||
remove_verb(src, list(
|
||||
GLOB.admin_verbs_default,
|
||||
/client/proc/togglebuildmodeself,
|
||||
GLOB.admin_verbs_admin,
|
||||
@@ -308,14 +309,15 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
GLOB.admin_verbs_debug_mapping,
|
||||
/client/proc/disable_debug_verbs,
|
||||
/client/proc/readmin
|
||||
)
|
||||
))
|
||||
|
||||
/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, GLOB.admin_verbs_hideable)
|
||||
verbs += /client/proc/show_verbs
|
||||
remove_verb(src, GLOB.admin_verbs_hideable)
|
||||
remove_verb(src, /client/proc/hide_most_verbs)
|
||||
add_verb(src, /client/proc/show_verbs)
|
||||
|
||||
to_chat(src, "<span class='interface'>Most of your adminverbs have been hidden.</span>")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide Most Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -326,7 +328,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
set category = "Admin"
|
||||
|
||||
remove_admin_verbs()
|
||||
verbs += /client/proc/show_verbs
|
||||
add_verb(src, /client/proc/show_verbs)
|
||||
|
||||
to_chat(src, "<span class='interface'>Almost all of your adminverbs have been hidden.</span>")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
@@ -336,7 +338,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
set name = "Adminverbs - Show"
|
||||
set category = "Admin"
|
||||
|
||||
verbs -= /client/proc/show_verbs
|
||||
remove_verb(src, /client/proc/show_verbs)
|
||||
add_admin_verbs()
|
||||
|
||||
to_chat(src, "<span class='interface'>All of your adminverbs are now visible.</span>")
|
||||
@@ -724,3 +726,9 @@ GLOBAL_PROTECT(admin_verbs_hideable)
|
||||
|
||||
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")
|
||||
|
||||
/client/proc/debugstatpanel()
|
||||
set name = "Debug Stat Panel"
|
||||
set category = "Debug"
|
||||
|
||||
src << output("", "statbrowser:create_debug")
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
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()
|
||||
var/random_eye_color = random_eye_color()
|
||||
H.left_eye_color = random_eye_color
|
||||
H.right_eye_color = random_eye_color
|
||||
H.dna.blood_type = random_blood_type()
|
||||
H.saved_underwear = H.underwear
|
||||
H.saved_undershirt = H.undershirt
|
||||
|
||||
@@ -91,7 +91,7 @@ GLOBAL_PROTECT(href_token)
|
||||
var/client/C
|
||||
if ((C = owner) || (C = GLOB.directory[target]))
|
||||
disassociate()
|
||||
C.verbs += /client/proc/readmin
|
||||
add_verb(C, /client/proc/readmin)
|
||||
|
||||
/datum/admins/proc/associate(client/C)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
@@ -111,7 +111,8 @@ GLOBAL_PROTECT(href_token)
|
||||
owner = C
|
||||
owner.holder = src
|
||||
owner.add_admin_verbs() //TODO <--- todo what? the proc clearly exists and works since its the backbone to our entire admin system
|
||||
owner.verbs -= /client/proc/readmin
|
||||
remove_verb(owner, /client/proc/readmin)
|
||||
owner.init_verbs() //re-initialize the verb list
|
||||
GLOB.admins |= C
|
||||
|
||||
/datum/admins/proc/disassociate()
|
||||
@@ -123,6 +124,7 @@ GLOBAL_PROTECT(href_token)
|
||||
if(owner)
|
||||
GLOB.admins -= owner
|
||||
owner.remove_admin_verbs()
|
||||
owner.init_verbs()
|
||||
owner.holder = null
|
||||
owner = null
|
||||
|
||||
|
||||
@@ -397,11 +397,13 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
|
||||
delete_click = new(null, "INITIALIZING", src)
|
||||
if(!action_click)
|
||||
action_click = new(null, "INITIALIZNG", src)
|
||||
stat("[id] ", delete_click.update("DELETE QUERY | STATE : [text_state()] | ALL/ELIG/FIN \
|
||||
var/list/L = list()
|
||||
L[++L.len] = list("[id] ", "[delete_click.update("DELETE QUERY | STATE : [text_state()] | ALL/ELIG/FIN \
|
||||
[islist(obj_count_all)? length(obj_count_all) : (isnull(obj_count_all)? "0" : obj_count_all)]/\
|
||||
[islist(obj_count_eligible)? length(obj_count_eligible) : (isnull(obj_count_eligible)? "0" : obj_count_eligible)]/\
|
||||
[islist(obj_count_finished)? length(obj_count_finished) : (isnull(obj_count_finished)? "0" : obj_count_finished)] - [get_query_text()]"))
|
||||
stat(" ", action_click.update("[SDQL2_IS_RUNNING? "HALT" : "RUN"]"))
|
||||
[islist(obj_count_finished)? length(obj_count_finished) : (isnull(obj_count_finished)? "0" : obj_count_finished)] - [get_query_text()]")]", REF(delete_click))
|
||||
L[++L.len] = list(" ", "[action_click.update("[SDQL2_IS_RUNNING? "HALT" : "RUN"]")]", REF(action_click))
|
||||
return L
|
||||
|
||||
/datum/SDQL2_query/proc/delete_click()
|
||||
admin_del(usr)
|
||||
@@ -1189,15 +1191,27 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
|
||||
return query_list
|
||||
|
||||
/obj/effect/statclick/SDQL2_delete/Click()
|
||||
if(!usr.client?.holder)
|
||||
message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
|
||||
log_game("[key_name(usr)] non-holder clicked on a statclick! ([src])")
|
||||
return
|
||||
var/datum/SDQL2_query/Q = target
|
||||
Q.delete_click()
|
||||
|
||||
/obj/effect/statclick/SDQL2_action/Click()
|
||||
if(!usr.client?.holder)
|
||||
message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
|
||||
log_game("[key_name(usr)] non-holder clicked on a statclick! ([src])")
|
||||
return
|
||||
var/datum/SDQL2_query/Q = target
|
||||
Q.action_click()
|
||||
|
||||
/obj/effect/statclick/SDQL2_VV_all
|
||||
name = "VIEW VARIABLES"
|
||||
|
||||
/obj/effect/statclick/SDQL2_VV_all/Click()
|
||||
/obj/effect/statclick/sdql2_vv_all/Click()
|
||||
if(!usr.client?.holder)
|
||||
message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
|
||||
log_game("[key_name(usr)] non-holder clicked on a statclick! ([src])")
|
||||
return
|
||||
usr.client.debug_variables(GLOB.sdql2_queries)
|
||||
|
||||
@@ -89,18 +89,23 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
|
||||
//Tickets statpanel
|
||||
/datum/admin_help_tickets/proc/stat_entry()
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
var/list/L = list()
|
||||
var/num_disconnected = 0
|
||||
stat("Active Tickets:", astatclick.update("[active_tickets.len]"))
|
||||
L[++L.len] = list("Active Tickets:", "[astatclick.update("[active_tickets.len]")]", null, REF(astatclick))
|
||||
astatclick.update("[active_tickets.len]")
|
||||
for(var/I in active_tickets)
|
||||
var/datum/admin_help/AH = I
|
||||
if(AH.initiator)
|
||||
stat("#[AH.id]. [AH.initiator_key_name]:", AH.statclick.update())
|
||||
L[++L.len] = list("#[AH.id]. [AH.initiator_key_name]:", "[AH.statclick.update()]", REF(AH))
|
||||
else
|
||||
++num_disconnected
|
||||
if(num_disconnected)
|
||||
stat("Disconnected:", astatclick.update("[num_disconnected]"))
|
||||
stat("Closed Tickets:", cstatclick.update("[closed_tickets.len]"))
|
||||
stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]"))
|
||||
L[++L.len] = list("Disconnected:", "[astatclick.update("[num_disconnected]")]", null, REF(astatclick))
|
||||
L[++L.len] = list("Closed Tickets:", "[cstatclick.update("[closed_tickets.len]")]", null, REF(cstatclick))
|
||||
L[++L.len] = list("Resolved Tickets:", "[rstatclick.update("[resolved_tickets.len]")]", null, REF(rstatclick))
|
||||
return L
|
||||
|
||||
//Reassociate still open ticket if one exists
|
||||
/datum/admin_help_tickets/proc/ClientLogin(client/C)
|
||||
@@ -137,6 +142,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
/obj/effect/statclick/ticket_list/Click()
|
||||
GLOB.ahelp_tickets.BrowseTickets(current_state)
|
||||
|
||||
//called by admin topic
|
||||
/obj/effect/statclick/ticket_list/proc/Action()
|
||||
Click()
|
||||
|
||||
//
|
||||
//TICKET DATUM
|
||||
//
|
||||
@@ -218,7 +227,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
|
||||
//Removes the ahelp verb and returns it after 2 minutes
|
||||
/datum/admin_help/proc/TimeoutVerb()
|
||||
initiator.verbs -= /client/verb/adminhelp
|
||||
remove_verb(initiator, /client/verb/adminhelp)
|
||||
initiator.adminhelptimerid = addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 1200, TIMER_STOPPABLE) //2 minute cooldown of admin helps
|
||||
|
||||
//private
|
||||
@@ -490,7 +499,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
//
|
||||
|
||||
/client/proc/giveadminhelpverb()
|
||||
src.verbs |= /client/verb/adminhelp
|
||||
add_verb(src, /client/verb/adminhelp)
|
||||
deltimer(adminhelptimerid)
|
||||
adminhelptimerid = 0
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set desc = "Area to jump to"
|
||||
set category = "Admin"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!A)
|
||||
@@ -15,20 +15,22 @@
|
||||
continue
|
||||
turfs.Add(T)
|
||||
|
||||
var/turf/T = safepick(turfs)
|
||||
if(!T)
|
||||
to_chat(src, "Nowhere to jump to!")
|
||||
if(length(turfs))
|
||||
var/turf/T = pick(turfs)
|
||||
usr.forceMove(T)
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
else
|
||||
to_chat(src, "Nowhere to jump to!", confidential = TRUE)
|
||||
return
|
||||
usr.forceMove(T)
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(A)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(A)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //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)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
|
||||
@@ -42,7 +44,7 @@
|
||||
set name = "Jump to Mob"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
log_admin("[key_name(usr)] jumped to [key_name(M)]")
|
||||
@@ -54,14 +56,14 @@
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
A.forceMove(M.loc)
|
||||
else
|
||||
to_chat(A, "This mob is not located in the game world.")
|
||||
to_chat(A, "This mob is not located in the game world.", confidential = TRUE)
|
||||
|
||||
/client/proc/jumptocoord(tx as num, ty as num, tz as num)
|
||||
set category = "Admin"
|
||||
set name = "Jump to Coordinate"
|
||||
|
||||
if (!holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(src.mob)
|
||||
@@ -76,7 +78,7 @@
|
||||
set name = "Jump to Key"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/keys = list()
|
||||
@@ -84,7 +86,7 @@
|
||||
keys += M.client
|
||||
var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
|
||||
if(!selection)
|
||||
to_chat(src, "No keys found.")
|
||||
to_chat(src, "No keys found.", confidential = TRUE)
|
||||
return
|
||||
var/mob/M = selection.mob
|
||||
log_admin("[key_name(usr)] jumped to [key_name(M)]")
|
||||
@@ -99,7 +101,7 @@
|
||||
set name = "Get Mob"
|
||||
set desc = "Mob to teleport"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/atom/loc = get_turf(usr)
|
||||
@@ -116,7 +118,7 @@
|
||||
set desc = "Key to teleport"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/keys = list()
|
||||
@@ -142,16 +144,17 @@
|
||||
set category = "Admin"
|
||||
set name = "Send Mob"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
var/area/A = input(usr, "Pick an area.", "Pick an area") in GLOB.sortedAreas|null
|
||||
if(A && istype(A))
|
||||
if(M.forceMove(safepick(get_area_turfs(A))))
|
||||
var/list/turfs = get_area_turfs(A)
|
||||
if(length(turfs) && M.forceMove(pick(turfs)))
|
||||
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(A)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(A)]"
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(M)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(M)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
else
|
||||
to_chat(src, "Failed to move mob to a valid location.")
|
||||
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#define IRCREPLYCOUNT 2
|
||||
|
||||
#define EXTERNALREPLYCOUNT 2
|
||||
|
||||
//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 GLOB.mob_list)
|
||||
set category = null
|
||||
set name = "Admin PM Mob"
|
||||
if(!holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>", confidential = TRUE)
|
||||
return
|
||||
if( !ismob(M) || !M.client )
|
||||
return
|
||||
@@ -18,7 +17,7 @@
|
||||
set category = "Admin"
|
||||
set name = "Admin PM"
|
||||
if(!holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>", confidential = TRUE)
|
||||
return
|
||||
var/list/client/targets[0]
|
||||
for(var/client/T)
|
||||
@@ -37,7 +36,7 @@
|
||||
|
||||
/client/proc/cmd_ahelp_reply(whom)
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
var/client/C
|
||||
if(istext(whom))
|
||||
@@ -48,45 +47,61 @@
|
||||
C = whom
|
||||
if(!C)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/datum/admin_help/AH = C.current_ticket
|
||||
|
||||
if(AH)
|
||||
message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.")
|
||||
message_admins("[key_name_admin(src)] has started replying to [key_name_admin(C, 0, 0)]'s admin help.")
|
||||
var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null
|
||||
if (!msg)
|
||||
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
|
||||
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name_admin(C, 0, 0)]'s admin help.")
|
||||
return
|
||||
if(!C) //We lost the client during input, disconnected or relogged.
|
||||
if(GLOB.directory[AH.initiator_ckey]) // Client has reconnected, lets try to recover
|
||||
whom = GLOB.directory[AH.initiator_ckey]
|
||||
else
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[msg]", confidential = TRUE)
|
||||
AH.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
|
||||
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)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
|
||||
to_chat(src, "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>")
|
||||
to_chat(src, "<span class='notice'>Message: [msg]</span>")
|
||||
to_chat(src, "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Message: [msg]</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/client/recipient
|
||||
var/irc = 0
|
||||
var/recipient_ckey // Stored in case client is deleted between this and after the message is input
|
||||
var/datum/admin_help/recipient_ticket // Stored in case client is deleted between this and after the message is input
|
||||
var/external = 0
|
||||
if(istext(whom))
|
||||
if(whom[1] == "@")
|
||||
whom = findStealthKey(whom)
|
||||
if(whom == "IRCKEY")
|
||||
irc = 1
|
||||
external = 1
|
||||
else
|
||||
recipient = GLOB.directory[whom]
|
||||
else if(istype(whom, /client))
|
||||
recipient = whom
|
||||
|
||||
if(!recipient)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(irc)
|
||||
recipient_ckey = recipient.ckey
|
||||
recipient_ticket = recipient.current_ticket
|
||||
|
||||
if(external)
|
||||
if(!ircreplyamount) //to prevent people from spamming irc/discord
|
||||
return
|
||||
if(!msg)
|
||||
@@ -95,21 +110,11 @@
|
||||
if(!msg)
|
||||
return
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Use the admin IRC channel, nerd.</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
|
||||
else
|
||||
if(!recipient)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>")
|
||||
if(msg)
|
||||
to_chat(src, msg)
|
||||
return
|
||||
else if(msg) // you want to continue if there's no message instead of returning now
|
||||
current_ticket.MessageNoRecipient(msg)
|
||||
return
|
||||
|
||||
//get message text, limit it's length.and clean/escape html
|
||||
if(!msg)
|
||||
msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null
|
||||
@@ -117,22 +122,30 @@
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>")
|
||||
return
|
||||
|
||||
if(!recipient)
|
||||
if(!recipient)
|
||||
if(GLOB.directory[recipient_ckey]) // Client has reconnected, lets try to recover
|
||||
recipient = GLOB.directory[recipient_ckey]
|
||||
else
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='danger'><b>Message not sent:</b></span><br>[msg]", confidential = TRUE)
|
||||
if(recipient_ticket)
|
||||
recipient_ticket.AddInteraction("<b>No client found, message not sent:</b><br>[msg]")
|
||||
return
|
||||
else
|
||||
current_ticket.MessageNoRecipient(msg)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
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)||irc)//no sending html to the poor bots
|
||||
if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
|
||||
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return
|
||||
@@ -144,28 +157,33 @@
|
||||
|
||||
var/keywordparsedmsg = keywords_lookup(msg)
|
||||
|
||||
if(irc)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>")
|
||||
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)] to <i>IRC</i>: [keywordparsedmsg]</font>")
|
||||
if(external)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>", confidential = TRUE)
|
||||
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)]</b> to <i>External</i>: [keywordparsedmsg]</font>")
|
||||
ircreplyamount--
|
||||
send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
|
||||
|
||||
else
|
||||
if(recipient.holder)
|
||||
if(holder) //both are admins
|
||||
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>")
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>")
|
||||
var/badmin = FALSE //Lets figure out if an admin is getting bwoinked.
|
||||
if(holder && recipient.holder && !current_ticket) //Both are admins, and this is not a reply to our own ticket.
|
||||
badmin = TRUE
|
||||
if(recipient.holder && !badmin)
|
||||
if(holder)
|
||||
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
|
||||
|
||||
//omg this is dumb, just fill in both their tickets
|
||||
var/interaction_message = "<font color='purple'>PM from-<b>[key_name(src, recipient, 1)]</b> to-<b>[key_name(recipient, src, 1)]</b>: [keywordparsedmsg]</font>"
|
||||
admin_ticket_log(src, interaction_message)
|
||||
if(recipient != src) //reeee
|
||||
admin_ticket_log(recipient, interaction_message)
|
||||
|
||||
// SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
else //recipient is an admin but sender is not
|
||||
var/replymsg = "Reply PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span>"
|
||||
admin_ticket_log(src, "<font color='red'>[replymsg]</font>")
|
||||
to_chat(recipient, "<span class='danger'>[replymsg]</span>")
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>")
|
||||
to_chat(recipient, "<span class='danger'>[replymsg]</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
// SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
|
||||
//play the receiving admin the adminhelp sound (if they have them enabled)
|
||||
if(recipient.prefs.toggles & SOUND_ADMINHELP)
|
||||
@@ -173,78 +191,88 @@
|
||||
|
||||
else
|
||||
if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
|
||||
//var/already_logged = FALSE
|
||||
if(!recipient.current_ticket)
|
||||
new /datum/admin_help(msg, recipient, TRUE)
|
||||
//already_logged = TRUE
|
||||
// SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey)
|
||||
|
||||
to_chat(recipient, "<font color='red' size='4'><b>-- Administrator private message --</b></font>")
|
||||
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>")
|
||||
to_chat(recipient, "<span class='danger'><i>Click on the administrator's name to reply.</i></span>")
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>")
|
||||
to_chat(recipient, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
|
||||
to_chat(recipient, "<span class='adminsay'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
to_chat(recipient, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
|
||||
admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>")
|
||||
|
||||
// if(!already_logged) //Reply to an existing ticket
|
||||
// SSblackbox.LogAhelp(recipient.current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
|
||||
|
||||
//always play non-admin recipients the adminhelp sound
|
||||
SEND_SOUND(recipient, sound('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_GET(flag/popup_admin_pm))
|
||||
spawn() //so we don't hold the caller proc up. Please functionalize this
|
||||
var/sender = src
|
||||
var/sendername = key
|
||||
var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply
|
||||
if(recipient && reply)
|
||||
if(sender)
|
||||
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
|
||||
else
|
||||
adminhelp(reply) //sender has left, adminhelp instead
|
||||
return
|
||||
INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg)
|
||||
|
||||
else //neither are admins
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>")
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(irc)
|
||||
log_admin_private("PM: [key_name(src)]->IRC: [rawmsg]")
|
||||
if(external)
|
||||
log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
|
||||
for(var/client/X in GLOB.admins)
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->IRC:</B> [keywordparsedmsg]</span>")
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->External:</B> [keywordparsedmsg]</span>", confidential = TRUE)
|
||||
else
|
||||
window_flash(recipient, ignorepref = TRUE)
|
||||
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
|
||||
//we don't use message_admins here because the sender/receiver might get it too
|
||||
for(var/client/X in GLOB.admins)
|
||||
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" )
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" , confidential = TRUE)
|
||||
|
||||
/client/proc/popup_admin_pm(client/recipient, msg)
|
||||
var/sender = src
|
||||
var/sendername = key
|
||||
var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply
|
||||
if(recipient && reply)
|
||||
if(sender)
|
||||
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
|
||||
else
|
||||
adminhelp(reply) //sender has left, adminhelp instead
|
||||
|
||||
|
||||
|
||||
#define IRC_AHELP_USAGE "Usage: ticket <close|resolve|icissue|reject|reopen \[ticket #\]|list>"
|
||||
/proc/IrcPm(target,msg,sender)
|
||||
return TgsPm(target,msg,sender) //compatability moment.
|
||||
|
||||
#define TGS_AHELP_USAGE "Usage: ticket <close|resolve|icissue|reject|reopen \[ticket #\]|list>"
|
||||
/proc/TgsPm(target,msg,sender)
|
||||
target = ckey(target)
|
||||
var/client/C = GLOB.directory[target]
|
||||
|
||||
var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target)
|
||||
var/compliant_msg = trim(lowertext(msg))
|
||||
var/irc_tagged = "[sender](IRC)"
|
||||
var/tgs_tagged = "[sender](TGS/External)"
|
||||
var/list/splits = splittext(compliant_msg, " ")
|
||||
if(splits.len && splits[1] == "ticket")
|
||||
if(splits.len < 2)
|
||||
return IRC_AHELP_USAGE
|
||||
return TGS_AHELP_USAGE
|
||||
switch(splits[2])
|
||||
if("close")
|
||||
if(ticket)
|
||||
ticket.Close(irc_tagged)
|
||||
ticket.Close(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully closed"
|
||||
if("resolve")
|
||||
if(ticket)
|
||||
ticket.Resolve(irc_tagged)
|
||||
ticket.Resolve(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully resolved"
|
||||
if("icissue")
|
||||
if(ticket)
|
||||
ticket.ICIssue(irc_tagged)
|
||||
ticket.ICIssue(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully marked as IC issue"
|
||||
if("reject")
|
||||
if(ticket)
|
||||
ticket.Reject(irc_tagged)
|
||||
ticket.Reject(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully rejected"
|
||||
if("reopen")
|
||||
if(ticket)
|
||||
@@ -253,7 +281,7 @@
|
||||
if(!isnull(fail))
|
||||
fail = text2num(splits[3])
|
||||
if(isnull(fail))
|
||||
return "Error: No/Invalid ticket id specified. [IRC_AHELP_USAGE]"
|
||||
return "Error: No/Invalid ticket id specified. [TGS_AHELP_USAGE]"
|
||||
var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(fail)
|
||||
if(!AH)
|
||||
return "Error: Ticket #[fail] not found"
|
||||
@@ -275,41 +303,42 @@
|
||||
. += "#[AH.id]"
|
||||
return
|
||||
else
|
||||
return IRC_AHELP_USAGE
|
||||
return TGS_AHELP_USAGE
|
||||
return "Error: Ticket could not be found"
|
||||
|
||||
var/static/stealthkey
|
||||
var/adminname = CONFIG_GET(flag/show_irc_name) ? irc_tagged : "Administrator"
|
||||
var/adminname = CONFIG_GET(flag/show_irc_name) ? tgs_tagged : "Administrator"
|
||||
|
||||
if(!C)
|
||||
return "Error: No client"
|
||||
|
||||
if(!stealthkey)
|
||||
stealthkey = GenIrcStealthKey()
|
||||
stealthkey = GenTgsStealthKey()
|
||||
|
||||
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return "Error: No message"
|
||||
|
||||
message_admins("IRC message from [sender] to [key_name_admin(C)] : [msg]")
|
||||
log_admin_private("IRC PM: [sender] -> [key_name(C)] : [msg]")
|
||||
message_admins("External message from [sender] to [key_name_admin(C)] : [msg]")
|
||||
log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]")
|
||||
msg = emoji_parse(msg)
|
||||
|
||||
to_chat(C, "<font color='red' size='4'><b>-- Administrator private message --</b></font>")
|
||||
to_chat(C, "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>")
|
||||
to_chat(C, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>")
|
||||
to_chat(C, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
|
||||
to_chat(C, "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>", confidential = TRUE)
|
||||
to_chat(C, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
|
||||
|
||||
admin_ticket_log(C, "<font color='purple'>PM From [irc_tagged]: [msg]</font>")
|
||||
admin_ticket_log(C, "<font color='purple'>PM From [tgs_tagged]: [msg]</font>")
|
||||
|
||||
window_flash(C, ignorepref = TRUE)
|
||||
//always play non-admin recipients the adminhelp sound
|
||||
SEND_SOUND(C, 'sound/effects/adminhelp.ogg')
|
||||
|
||||
C.ircreplyamount = IRCREPLYCOUNT
|
||||
// C.externalreplyamount = EXTERNALREPLYCOUNT
|
||||
C.ircreplyamount = EXTERNALREPLYCOUNT
|
||||
|
||||
return "Message Successful"
|
||||
|
||||
/proc/GenIrcStealthKey()
|
||||
/proc/GenTgsStealthKey()
|
||||
var/num = (rand(0,1000))
|
||||
var/i = 0
|
||||
while(i == 0)
|
||||
@@ -322,4 +351,4 @@
|
||||
GLOB.stealthminID["IRCKEY"] = stealth
|
||||
return stealth
|
||||
|
||||
#undef IRCREPLYCOUNT
|
||||
#undef EXTERNALREPLYCOUNT
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
msg = keywords_lookup(msg)
|
||||
msg = "<span class='adminsay'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: <span class='message linkify'>[msg]</span></span>"
|
||||
to_chat(GLOB.admins, msg)
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
var/mob/living/target = M
|
||||
|
||||
if(!isliving(target))
|
||||
to_chat(usr, "This can only be used on instances of type /mob/living")
|
||||
to_chat(usr, "This can only be used on instances of type /mob/living", confidential = TRUE)
|
||||
return
|
||||
|
||||
explosion(target.loc, 0, 0, 0, 0)
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
return
|
||||
|
||||
if (!istype(borgo, /mob/living/silicon/robot))
|
||||
borgo = input("Select a borg", "Select a borg", null, null) as null|anything in GLOB.silicon_mobs
|
||||
borgo = input("Select a borg", "Select a borg", null, null) as null|anything in sortNames(GLOB.silicon_mobs)
|
||||
if (!istype(borgo, /mob/living/silicon/robot))
|
||||
to_chat(usr, "<span class='warning'>Borg is required for borgpanel</span>")
|
||||
to_chat(usr, "<span class='warning'>Borg is required for borgpanel</span>", confidential = TRUE)
|
||||
|
||||
var/datum/borgpanel/borgpanel = new(usr, borgo)
|
||||
|
||||
@@ -25,18 +25,18 @@
|
||||
if(!istype(to_borg))
|
||||
qdel(src)
|
||||
CRASH("Borg panel is only available for borgs")
|
||||
|
||||
user = CLIENT_FROM_VAR(to_user)
|
||||
|
||||
if (!user)
|
||||
CRASH("Borg panel attempted to open to a mob without a client")
|
||||
|
||||
borg = to_borg
|
||||
|
||||
/datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/datum/borgpanel/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/borgpanel/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "BorgPanel", "Borg Panel", 700, 700, master_ui, state)
|
||||
ui = new(user, src, "BorgPanel")
|
||||
ui.open()
|
||||
|
||||
/datum/borgpanel/ui_data(mob/user)
|
||||
@@ -53,13 +53,13 @@
|
||||
.["upgrades"] = list()
|
||||
for (var/upgradetype in subtypesof(/obj/item/borg/upgrade)-/obj/item/borg/upgrade/hypospray) //hypospray is a dummy parent for hypospray upgrades
|
||||
var/obj/item/borg/upgrade/upgrade = upgradetype
|
||||
if (initial(upgrade.module_type) && !istype(borg.module, initial(upgrade.module_type))) // Upgrade requires a different module
|
||||
if (initial(upgrade.module_type) && !is_type_in_list(borg.module, initial(upgrade.module_type))) // Upgrade requires a different module
|
||||
continue
|
||||
var/installed = FALSE
|
||||
if (locate(upgradetype) in borg)
|
||||
installed = TRUE
|
||||
.["upgrades"] += list(list("name" = initial(upgrade.name), "installed" = installed, "type" = upgradetype))
|
||||
.["laws"] = borg.laws ? borg.laws.get_law_list(include_zeroth = TRUE) : list()
|
||||
.["laws"] = borg.laws ? borg.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE) : list()
|
||||
.["channels"] = list()
|
||||
for (var/k in GLOB.radiochannels)
|
||||
if (k == RADIO_CHANNEL_COMMON)
|
||||
@@ -199,7 +199,7 @@
|
||||
borg.notify_ai(DISCONNECT)
|
||||
if(borg.shell)
|
||||
borg.undeploy()
|
||||
borg.connected_ai = newai
|
||||
borg.set_connected_ai(newai)
|
||||
borg.notify_ai(TRUE)
|
||||
message_admins("[key_name_admin(user)] slaved [ADMIN_LOOKUPFLW(borg)] to the AI [ADMIN_LOOKUPFLW(newai)].")
|
||||
log_admin("[key_name(user)] slaved [key_name(borg)] to the AI [key_name(newai)].")
|
||||
@@ -207,7 +207,7 @@
|
||||
borg.notify_ai(DISCONNECT)
|
||||
if(borg.shell)
|
||||
borg.undeploy()
|
||||
borg.connected_ai = null
|
||||
borg.set_connected_ai(null)
|
||||
message_admins("[key_name_admin(user)] freed [ADMIN_LOOKUPFLW(borg)] from being slaved to an AI.")
|
||||
log_admin("[key_name(user)] freed [key_name(borg)] from being slaved to an AI.")
|
||||
if (borg.lawupdate)
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
set category = "Special Verbs"
|
||||
set name = "Dsay"
|
||||
set hidden = 1
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
if(!holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
if(!src.mob)
|
||||
if(!mob)
|
||||
return
|
||||
if(prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='danger'>You cannot send DSAY messages (muted).</span>")
|
||||
to_chat(src, "<span class='danger'>You cannot send DSAY messages (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (src.handle_spam_prevention(msg,MUTE_DEADCHAT))
|
||||
if (handle_spam_prevention(msg,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
|
||||
@@ -23,14 +23,25 @@
|
||||
|
||||
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
|
||||
|
||||
// var/rank_name = holder.rank
|
||||
// var/admin_name = key
|
||||
// if(holder.fakekey)
|
||||
// rank_name = pick(strings("admin_nicknames.json", "ranks", "config")) please use this soon.
|
||||
// admin_name = pick(strings("admin_nicknames.json", "names", "config"))
|
||||
// var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[rank_name]([admin_name])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
|
||||
|
||||
for (var/mob/M in GLOB.player_list)
|
||||
if(isnewplayer(M))
|
||||
continue
|
||||
if (M.stat == DEAD || (M.client && M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
|
||||
to_chat(M, rendered)
|
||||
if (M.stat == DEAD || (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
|
||||
to_chat(M, rendered, confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Dsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/get_dead_say()
|
||||
var/msg = input(src, null, "dsay \"text\"") as text
|
||||
var/msg = input(src, null, "dsay \"text\"") as text|null
|
||||
|
||||
if (isnull(msg))
|
||||
return
|
||||
|
||||
dsay(msg)
|
||||
|
||||
@@ -31,5 +31,5 @@
|
||||
src << ftp(file(path))
|
||||
else
|
||||
return
|
||||
to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.")
|
||||
return
|
||||
to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.", confidential = TRUE)
|
||||
return
|
||||
|
||||
@@ -205,15 +205,15 @@ GLOBAL_LIST_EMPTY(dirty_vars)
|
||||
set name = "Debug verbs - Enable"
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
verbs -= /client/proc/enable_debug_verbs
|
||||
verbs.Add(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
|
||||
remove_verb(src, /client/proc/enable_debug_verbs)
|
||||
add_verb(src, list(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping))
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Enable Debug Verbs") //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, GLOB.admin_verbs_debug_mapping)
|
||||
verbs += /client/proc/enable_debug_verbs
|
||||
remove_verb(src, list(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping))
|
||||
add_verb(src, /client/proc/enable_debug_verbs)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Disable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/count_objects_on_z_level()
|
||||
|
||||
@@ -34,9 +34,7 @@
|
||||
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client.prefs.toggles & SOUND_MIDI)
|
||||
var/user_vol = M.client.chatOutput.adminMusicVolume
|
||||
if(user_vol)
|
||||
admin_sound.volume = vol * (user_vol / 100)
|
||||
admin_sound.volume = vol * M.client.admin_music_volume
|
||||
SEND_SOUND(M, admin_sound)
|
||||
admin_sound.volume = vol
|
||||
|
||||
@@ -69,7 +67,7 @@
|
||||
if(istext(web_sound_input))
|
||||
var/web_sound_url = ""
|
||||
var/stop_web_sounds = FALSE
|
||||
var/pitch
|
||||
var/list/music_extra_data = list()
|
||||
if(length(web_sound_input))
|
||||
|
||||
web_sound_input = trim(web_sound_input)
|
||||
@@ -97,11 +95,10 @@
|
||||
var/webpage_url = title
|
||||
if (data["webpage_url"])
|
||||
webpage_url = "<a href=\"[data["webpage_url"]]\">[title]</a>"
|
||||
|
||||
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
|
||||
if(!freq)
|
||||
freq = 1
|
||||
pitch = freq
|
||||
music_extra_data["start"] = data["start_time"]
|
||||
music_extra_data["end"] = data["end_time"]
|
||||
music_extra_data["link"] = data["webpage_url"]
|
||||
music_extra_data["title"] = data["title"]
|
||||
|
||||
var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel")
|
||||
switch(res)
|
||||
@@ -130,11 +127,11 @@
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
if(C.prefs.toggles & SOUND_MIDI)
|
||||
if(!stop_web_sounds)
|
||||
C.chatOutput.sendMusic(web_sound_url, pitch)
|
||||
C.tgui_panel?.play_music(web_sound_url, music_extra_data)
|
||||
else
|
||||
C.chatOutput.stopMusic()
|
||||
C.tgui_panel?.stop_music()
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
|
||||
|
||||
@@ -144,7 +141,7 @@
|
||||
if(!check_rights(R_SOUNDS))
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter content stream URL (fetch this from local youtube-dl!)", "Play Internet Sound via direct URL") as text|null
|
||||
var/web_sound_input = input("Enter content stream URL (must be a direct link)", "Play Internet Sound via direct URL") as text|null
|
||||
if(istext(web_sound_input))
|
||||
if(!length(web_sound_input))
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
@@ -152,34 +149,37 @@
|
||||
var/mob/M
|
||||
for(var/i in GLOB.player_list)
|
||||
M = i
|
||||
M?.client?.chatOutput?.stopMusic()
|
||||
M?.client?.tgui_panel?.stop_music()
|
||||
return
|
||||
else
|
||||
if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>")
|
||||
return
|
||||
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
|
||||
if(isnull(freq))
|
||||
return
|
||||
if(!freq)
|
||||
freq = 1
|
||||
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
|
||||
var/logstr = "[key_name(src)] played web sound at freq [freq]: [web_sound_input]"
|
||||
log_admin(logstr)
|
||||
message_admins(logstr)
|
||||
var/mob/M
|
||||
var/client/C
|
||||
var/datum/chatOutput/O
|
||||
for(var/i in GLOB.player_list)
|
||||
M = i
|
||||
C = M.client
|
||||
if(!(C?.prefs?.toggles & SOUND_MIDI))
|
||||
continue
|
||||
O = C.chatOutput
|
||||
if(!O || O.broken || !O.loaded)
|
||||
continue
|
||||
O.sendMusic(web_sound_input, freq)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound")
|
||||
|
||||
var/list/music_extra_data = list()
|
||||
web_sound_input = trim(web_sound_input)
|
||||
if(web_sound_input && (findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol)))
|
||||
to_chat(src, "<span class='boldwarning'>Non-http(s) URIs are not allowed.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/explode = splittext(web_sound_input, "/") //if url=="https://fixthisshit.com/pogchamp.ogg"then title="pogchamp.ogg"
|
||||
var/title = "[explode[explode.len]]"
|
||||
|
||||
if(!findtext(title, ".mp3") && !findtext(title, ".mp4")) // IE sucks.
|
||||
to_chat(src, "<span class='warning'>The format is not .mp3/.mp4, IE 8 and above can only support the .mp3/.mp4 format, the music might not play.</span>", confidential = TRUE)
|
||||
|
||||
if(length(title) > 50) //kev no.
|
||||
title = "Unknown.mp3"
|
||||
|
||||
music_extra_data["title"] = title
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
|
||||
log_admin("[key_name(src)] played web sound: [web_sound_input]")
|
||||
message_admins("[key_name(src)] played web sound: [web_sound_input]")
|
||||
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if(C.prefs.toggles & SOUND_MIDI)
|
||||
C.tgui_panel?.play_music(web_sound_input, music_extra_data)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound")
|
||||
|
||||
/client/proc/set_round_end_sound(S as sound)
|
||||
set category = "Fun"
|
||||
@@ -193,42 +193,6 @@
|
||||
message_admins("[key_name_admin(src)] set the round end sound to [S]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Round End Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_web_sound_manual()
|
||||
set category = "Fun"
|
||||
set name = "Manual Play Internet Sound"
|
||||
if(!check_rights(R_SOUNDS))
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter youtube-dl fetched content URL (supported sites only, leave blank to stop playing)", "Send youtube-dl media link") as text|null
|
||||
if(!istext(web_sound_input))
|
||||
return
|
||||
web_sound_input = trim(web_sound_input)
|
||||
if(!length(web_sound_input))
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
message_admins("[key_name(src)] stopped web sound")
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.stopMusic()
|
||||
return
|
||||
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
|
||||
if(!freq)
|
||||
return
|
||||
if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>")
|
||||
to_chat(src, "<span class='warning'>The media provider returned a content URL that isn't using the HTTP or HTTPS protocol</span>")
|
||||
return
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "played_url_manual", 1, list("[ckey]", "[web_sound_input]"))
|
||||
log_admin("[key_name(src)] manually played web sound: [web_sound_input]")
|
||||
message_admins("[key_name(src)] manually played web sound: <a href='web_sound_input'>HREF</a>")
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.sendMusic(web_sound_input, freq)
|
||||
|
||||
/client/proc/stop_sounds()
|
||||
set category = "Debug"
|
||||
set name = "Stop All Playing Sounds"
|
||||
@@ -238,9 +202,7 @@
|
||||
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 GLOB.player_list)
|
||||
if(M.client)
|
||||
SEND_SOUND(M, sound(null))
|
||||
var/client/C = M.client
|
||||
if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.stopMusic()
|
||||
SEND_SOUND(M, sound(null))
|
||||
var/client/C = M.client
|
||||
C?.tgui_panel?.stop_music()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop All Playing Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -48,6 +48,6 @@
|
||||
set desc = "Give this guy possess/release verbs"
|
||||
set category = "Debug"
|
||||
set name = "Give Possessing Verbs"
|
||||
M.verbs += /proc/possess
|
||||
M.verbs += /proc/release
|
||||
add_verb(M, /proc/possess)
|
||||
add_verb(M, /proc/release)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Possessing Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -646,23 +646,14 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/heavy = input("Range of heavy pulse.", text("Input")) as num|null
|
||||
if(heavy == null)
|
||||
return
|
||||
var/light = input("Range of light pulse.", text("Input")) as num|null
|
||||
if(light == null)
|
||||
var/range = input("Range.", text("Input")) as num|null
|
||||
if(!range)
|
||||
return
|
||||
log_admin("[key_name(usr)] created an EM Pulse - log below") //because we'll just log the empulse itself
|
||||
message_admins("[key_name_admin(usr)] created an EM Pulse at [AREACOORD(O)] with range [range]")
|
||||
empulse_using_range(O, range, TRUE) //its details get shown too
|
||||
|
||||
if (heavy || light)
|
||||
|
||||
empulse(O, heavy, light)
|
||||
log_admin("[key_name(usr)] created an EM Pulse ([heavy],[light]) at [AREACOORD(O)]")
|
||||
message_admins("[key_name_admin(usr)] created an EM Pulse ([heavy],[light]) at [AREACOORD(O)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "EM Pulse") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
return
|
||||
else
|
||||
return
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "EM Pulse") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_gib(mob/M in GLOB.mob_list)
|
||||
set category = "Special Verbs"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
var/var_value = O.vars[variable]
|
||||
|
||||
if(variable in GLOB.VVckey_edit)
|
||||
to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.")
|
||||
to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.", confidential = TRUE)
|
||||
return
|
||||
if(variable in GLOB.VVlocked)
|
||||
if(!check_rights(R_DEBUG))
|
||||
@@ -56,11 +56,11 @@
|
||||
default = vv_get_class(variable, var_value)
|
||||
|
||||
if(isnull(default))
|
||||
to_chat(src, "Unable to determine variable type.")
|
||||
to_chat(src, "Unable to determine variable type.", confidential = TRUE)
|
||||
else
|
||||
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.")
|
||||
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.", confidential = TRUE)
|
||||
|
||||
to_chat(src, "Variable contains: [var_value]")
|
||||
to_chat(src, "Variable contains: [var_value]", confidential = TRUE)
|
||||
|
||||
if(default == VV_NUM)
|
||||
var/dir_text = ""
|
||||
@@ -75,7 +75,7 @@
|
||||
dir_text += "WEST"
|
||||
|
||||
if(dir_text)
|
||||
to_chat(src, "If a direction, direction is: [dir_text]")
|
||||
to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE)
|
||||
|
||||
var/value = vv_get_value(default_class = default)
|
||||
var/new_value = value["value"]
|
||||
@@ -97,9 +97,9 @@
|
||||
|
||||
switch(class)
|
||||
if(VV_RESTORE_DEFAULT)
|
||||
to_chat(src, "Finding items...")
|
||||
to_chat(src, "Finding items...", confidential = TRUE)
|
||||
var/list/items = get_all_of_type(O.type, method)
|
||||
to_chat(src, "Changing [items.len] items...")
|
||||
to_chat(src, "Changing [items.len] items...", confidential = TRUE)
|
||||
for(var/thing in items)
|
||||
if (!thing)
|
||||
continue
|
||||
@@ -123,9 +123,9 @@
|
||||
for(var/V in varsvars)
|
||||
new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]")
|
||||
|
||||
to_chat(src, "Finding items...")
|
||||
to_chat(src, "Finding items...", confidential = TRUE)
|
||||
var/list/items = get_all_of_type(O.type, method)
|
||||
to_chat(src, "Changing [items.len] items...")
|
||||
to_chat(src, "Changing [items.len] items...", confidential = TRUE)
|
||||
for(var/thing in items)
|
||||
if (!thing)
|
||||
continue
|
||||
@@ -151,9 +151,9 @@
|
||||
many = FALSE
|
||||
|
||||
var/type = value["type"]
|
||||
to_chat(src, "Finding items...")
|
||||
to_chat(src, "Finding items...", confidential = TRUE)
|
||||
var/list/items = get_all_of_type(O.type, method)
|
||||
to_chat(src, "Changing [items.len] items...")
|
||||
to_chat(src, "Changing [items.len] items...", confidential = TRUE)
|
||||
for(var/thing in items)
|
||||
if (!thing)
|
||||
continue
|
||||
@@ -169,9 +169,9 @@
|
||||
CHECK_TICK
|
||||
|
||||
else
|
||||
to_chat(src, "Finding items...")
|
||||
to_chat(src, "Finding items...", confidential = TRUE)
|
||||
var/list/items = get_all_of_type(O.type, method)
|
||||
to_chat(src, "Changing [items.len] items...")
|
||||
to_chat(src, "Changing [items.len] items...", confidential = TRUE)
|
||||
for(var/thing in items)
|
||||
if (!thing)
|
||||
continue
|
||||
@@ -185,20 +185,20 @@
|
||||
|
||||
var/count = rejected+accepted
|
||||
if (!count)
|
||||
to_chat(src, "No objects found")
|
||||
to_chat(src, "No objects found", confidential = TRUE)
|
||||
return
|
||||
if (!accepted)
|
||||
to_chat(src, "Every object rejected your edit")
|
||||
to_chat(src, "Every object rejected your edit", confidential = TRUE)
|
||||
return
|
||||
if (rejected)
|
||||
to_chat(src, "[rejected] out of [count] objects rejected your edit")
|
||||
to_chat(src, "[rejected] out of [count] objects rejected your edit", confidential = TRUE)
|
||||
|
||||
log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])")
|
||||
log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)")
|
||||
message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)")
|
||||
|
||||
//not using global lists as vv is a debug function and debug functions should rely on as less things as possible.
|
||||
/proc/get_all_of_type(var/T, subtypes = TRUE)
|
||||
/proc/get_all_of_type(T, subtypes = TRUE)
|
||||
var/list/typecache = list()
|
||||
typecache[T] = 1
|
||||
if (subtypes)
|
||||
@@ -253,7 +253,7 @@
|
||||
CHECK_TICK
|
||||
|
||||
else if (ispath(T, /client))
|
||||
for(var/client/thing in world)
|
||||
for(var/client/thing in GLOB.clients)
|
||||
if (typecache[thing.type])
|
||||
. += thing
|
||||
CHECK_TICK
|
||||
|
||||
@@ -17,7 +17,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes)
|
||||
//TRUE = Yes subtypes
|
||||
//NULL = User cancelled at the prompt or invalid type given
|
||||
/client/proc/vv_subtype_prompt(var/type)
|
||||
/client/proc/vv_subtype_prompt(type)
|
||||
if (!ispath(type))
|
||||
return
|
||||
var/list/subtypes = subtypesof(type)
|
||||
@@ -102,7 +102,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
L[var_value] = mod_list_add_ass(O) //hehe
|
||||
if (O)
|
||||
if (O.vv_edit_var(objectvar, L) == FALSE)
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]")
|
||||
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
|
||||
@@ -112,7 +112,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
if(!check_rights(R_VAREDIT))
|
||||
return
|
||||
if(!istype(L, /list))
|
||||
to_chat(src, "Not a List.")
|
||||
to_chat(src, "Not a List.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(L.len > 1000)
|
||||
@@ -121,7 +121,6 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
return
|
||||
|
||||
var/is_normal_list = IS_NORMAL_LIST(L)
|
||||
|
||||
var/list/names = list()
|
||||
for (var/i in 1 to L.len)
|
||||
var/key = L[i]
|
||||
@@ -145,7 +144,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
L = L.Copy()
|
||||
listclearnulls(L)
|
||||
if (!O.vv_edit_var(objectvar, L))
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS")
|
||||
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS")
|
||||
@@ -155,7 +154,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
if(variable == "(CLEAR DUPES)")
|
||||
L = uniqueList(L)
|
||||
if (!O.vv_edit_var(objectvar, L))
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES")
|
||||
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES")
|
||||
@@ -165,7 +164,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
if(variable == "(SHUFFLE)")
|
||||
L = shuffle(L)
|
||||
if (!O.vv_edit_var(objectvar, L))
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE")
|
||||
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE")
|
||||
@@ -202,9 +201,9 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
|
||||
default = vv_get_class(objectvar, variable)
|
||||
|
||||
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.")
|
||||
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.", confidential = TRUE)
|
||||
|
||||
to_chat(src, "Variable contains: [variable]")
|
||||
to_chat(src, "Variable contains: [variable]", confidential = TRUE)
|
||||
|
||||
if(default == VV_NUM)
|
||||
var/dir_text = ""
|
||||
@@ -220,7 +219,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
dir_text += "WEST"
|
||||
|
||||
if(dir_text)
|
||||
to_chat(usr, "If a direction, direction is: [dir_text]")
|
||||
to_chat(usr, "If a direction, direction is: [dir_text]", confidential = TRUE)
|
||||
|
||||
var/original_var = variable
|
||||
|
||||
@@ -248,7 +247,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
L.Cut(index, index+1)
|
||||
if (O)
|
||||
if (O.vv_edit_var(objectvar, L))
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]")
|
||||
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
|
||||
@@ -260,6 +259,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
for(var/V in varsvars)
|
||||
new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]")
|
||||
|
||||
|
||||
if(is_normal_list)
|
||||
if(assoc)
|
||||
L[assoc_key] = new_var
|
||||
@@ -269,7 +269,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
L[new_var] = old_assoc_value
|
||||
if (O)
|
||||
if (O.vv_edit_var(objectvar, L) == FALSE)
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]")
|
||||
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
|
||||
@@ -297,7 +297,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
|
||||
if(param_var_name)
|
||||
if(!(param_var_name in O.vars))
|
||||
to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])")
|
||||
to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])", confidential = TRUE)
|
||||
return
|
||||
variable = param_var_name
|
||||
|
||||
@@ -322,11 +322,11 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
var/default = vv_get_class(variable, var_value)
|
||||
|
||||
if(isnull(default))
|
||||
to_chat(src, "Unable to determine variable type.")
|
||||
to_chat(src, "Unable to determine variable type.", confidential = TRUE)
|
||||
else
|
||||
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.")
|
||||
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.", confidential = TRUE)
|
||||
|
||||
to_chat(src, "Variable contains: [var_value]")
|
||||
to_chat(src, "Variable contains: [var_value]", confidential = TRUE)
|
||||
|
||||
if(default == VV_NUM)
|
||||
var/dir_text = ""
|
||||
@@ -341,7 +341,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
dir_text += "WEST"
|
||||
|
||||
if(dir_text)
|
||||
to_chat(src, "If a direction, direction is: [dir_text]")
|
||||
to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE)
|
||||
|
||||
if(autodetect_class && default != VV_NULL)
|
||||
if (default == VV_TEXT)
|
||||
@@ -378,7 +378,7 @@ GLOBAL_PROTECT(VVpixelmovement)
|
||||
|
||||
|
||||
if (O.vv_edit_var(variable, var_new) == FALSE)
|
||||
to_chat(src, "Your edit was rejected by the object.")
|
||||
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
|
||||
return
|
||||
vv_update_display(O, "varedited", VV_MSG_EDITED)
|
||||
log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]")
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
var/mob/M = locate(href_list["rename"]) in GLOB.mob_list
|
||||
if(!istype(M))
|
||||
to_chat(usr, "This can only be used on instances of type /mob")
|
||||
to_chat(usr, "This can only be used on instances of type /mob", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN)
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
var/atom/A = locate(href_list["rotatedatum"])
|
||||
if(!istype(A))
|
||||
to_chat(usr, "This can only be done to instances of type /atom")
|
||||
to_chat(usr, "This can only be done to instances of type /atom", confidential = TRUE)
|
||||
return
|
||||
|
||||
switch(href_list["rotatedir"])
|
||||
@@ -60,13 +60,13 @@
|
||||
|
||||
var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list
|
||||
if(!istype(Mo))
|
||||
to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey")
|
||||
to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
|
||||
return
|
||||
if(!Mo)
|
||||
to_chat(usr, "Mob doesn't exist anymore")
|
||||
to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE)
|
||||
return
|
||||
holder.Topic(href, list("humanone"=href_list["makehuman"]))
|
||||
|
||||
@@ -80,10 +80,13 @@
|
||||
|
||||
var/Text = href_list["adjustDamage"]
|
||||
|
||||
var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num
|
||||
var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num|null
|
||||
|
||||
if (isnull(amount))
|
||||
return
|
||||
|
||||
if(!L)
|
||||
to_chat(usr, "Mob doesn't exist anymore")
|
||||
to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/newamt
|
||||
@@ -110,7 +113,7 @@
|
||||
L.adjustStaminaLoss(amount)
|
||||
newamt = L.getStaminaLoss()
|
||||
else
|
||||
to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
|
||||
to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(amount != 0)
|
||||
@@ -124,5 +127,5 @@
|
||||
//Finally, refresh if something modified the list.
|
||||
if(href_list["datumrefresh"])
|
||||
var/datum/DAT = locate(href_list["datumrefresh"])
|
||||
if(istype(DAT, /datum) || istype(DAT, /client))
|
||||
if(istype(DAT, /datum) || istype(DAT, /client) || islist(DAT))
|
||||
debug_variables(DAT)
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
if (!C)
|
||||
return
|
||||
if(!target)
|
||||
to_chat(usr, "<span class='warning'>The object you tried to expose to [C] no longer exists (nulled or hard-deled)</span>")
|
||||
to_chat(usr, "<span class='warning'>The object you tried to expose to [C] no longer exists (nulled or hard-deled)</span>", confidential = TRUE)
|
||||
return
|
||||
message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a <a href='?_src_=vars;datumrefresh=[REF(target)]'>VV window</a>")
|
||||
log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [target]")
|
||||
to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
|
||||
to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window", confidential = TRUE)
|
||||
C.debug_variables(target)
|
||||
if(check_rights(R_DEBUG))
|
||||
if(href_list[VV_HK_DELETE])
|
||||
@@ -46,31 +46,33 @@
|
||||
if (isturf(src)) // show the turf that took its place
|
||||
usr.client.debug_variables(src)
|
||||
return
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(href_list[VV_HK_VIEW_REFERENCES])
|
||||
var/datum/D = locate(href_list[VV_HK_TARGET])
|
||||
if(!D)
|
||||
to_chat(usr, "<span class='warning'>Unable to locate item.</span>")
|
||||
|
||||
#ifdef REFERENCE_TRACKING //people with debug can only access this putnam!
|
||||
if(href_list[VV_HK_VIEW_REFERENCES])
|
||||
var/datum/D = locate(href_list[VV_HK_TARGET])
|
||||
if(!D)
|
||||
to_chat(usr, "<span class='warning'>Unable to locate item.</span>")
|
||||
return
|
||||
usr.client.holder.view_refs(target)
|
||||
return
|
||||
usr.client.holder.view_refs(target)
|
||||
return
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(href_list[VV_HK_MARK])
|
||||
usr.client.mark_datum(target)
|
||||
if(href_list[VV_HK_ADDCOMPONENT])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
var/list/names = list()
|
||||
var/list/componentsubtypes = subtypesof(/datum/component)
|
||||
var/list/componentsubtypes = sortList(subtypesof(/datum/component), /proc/cmp_typepaths_asc)
|
||||
names += "---Components---"
|
||||
names += componentsubtypes
|
||||
names += "---Elements---"
|
||||
names += subtypesof(/datum/element)
|
||||
names += sortList(subtypesof(/datum/element), /proc/cmp_typepaths_asc)
|
||||
var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names
|
||||
if(!usr || !result || result == "---Components---" || result == "---Elements---")
|
||||
return
|
||||
if(QDELETED(src))
|
||||
to_chat(usr, "That thing doesn't exist anymore!")
|
||||
to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE)
|
||||
return
|
||||
var/list/lst = get_callproc_args()
|
||||
if(!lst)
|
||||
@@ -83,7 +85,7 @@
|
||||
else
|
||||
datumname = "element"
|
||||
target._AddElement(lst)
|
||||
log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)].</span>")
|
||||
log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(target)].")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(target)].</span>")
|
||||
if(href_list[VV_HK_CALLPROC])
|
||||
usr.client.callproc_datum(target)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/client/proc/debug_variables(datum/D in world)
|
||||
set category = "Debug"
|
||||
set name = "View Variables"
|
||||
@@ -6,7 +5,7 @@
|
||||
var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round.
|
||||
|
||||
if(!usr.client || !usr.client.holder) //This is usr because admins can call the proc on other clients, even if they're not admins, to show them VVs.
|
||||
to_chat(usr, "<span class='danger'>You need to be an administrator to access this.</span>")
|
||||
to_chat(usr, "<span class='danger'>You need to be an administrator to access this.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!D)
|
||||
@@ -26,7 +25,6 @@
|
||||
|
||||
if(istype(D, /atom))
|
||||
sprite = getFlatIcon(D)
|
||||
hash = md5(sprite)
|
||||
if(sprite)
|
||||
hash = md5(sprite)
|
||||
src << browse_rsc(sprite, "vv[hash].png")
|
||||
@@ -97,7 +95,7 @@
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>[title]</title>
|
||||
<link rel="stylesheet" type="text/css" href="view_variables.css">
|
||||
<link rel="stylesheet" type="text/css" href="[SSassets.transport.get_asset_url("view_variables.css")]">
|
||||
</head>
|
||||
<body onload='selectTextField()' onkeydown='return handle_keydown()' onkeyup='handle_keyup()'>
|
||||
<script type="text/javascript">
|
||||
@@ -122,11 +120,13 @@
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// main search functionality
|
||||
var last_filter = "";
|
||||
function updateSearch() {
|
||||
var filter = document.getElementById('filter').value.toLowerCase();
|
||||
var vars_ol = document.getElementById("vars");
|
||||
|
||||
if (filter === last_filter) {
|
||||
// An event triggered an update but nothing has changed.
|
||||
return;
|
||||
@@ -146,6 +146,7 @@
|
||||
while (vars_ol.hasChildNodes()) {
|
||||
vars_ol.removeChild(vars_ol.lastChild);
|
||||
}
|
||||
|
||||
for (var i = 0; i < complete_list.length; ++i) {
|
||||
try {
|
||||
var li = complete_list\[i];
|
||||
@@ -155,9 +156,12 @@
|
||||
} catch(err) {}
|
||||
}
|
||||
}
|
||||
|
||||
last_filter = filter;
|
||||
document.cookie="[refid][cookieoffset]search="+encodeURIComponent(filter);
|
||||
|
||||
}
|
||||
|
||||
// onkeydown
|
||||
function handle_keydown() {
|
||||
if(event.keyCode == 116) { //F5 (to refresh properly)
|
||||
@@ -167,10 +171,12 @@
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// onkeyup
|
||||
function handle_keyup() {
|
||||
updateSearch();
|
||||
}
|
||||
|
||||
// onchange
|
||||
function handle_dropdown(list) {
|
||||
var value = list.options\[list.selectedIndex].value;
|
||||
@@ -180,6 +186,7 @@
|
||||
list.selectedIndex = 0;
|
||||
document.getElementById('filter').focus();
|
||||
}
|
||||
|
||||
// byjax
|
||||
function replace_span(what) {
|
||||
var idx = what.indexOf(':');
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
/datum/blobstrain/reagent/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag)
|
||||
if(damage_flag == "melee" || damage_flag == "bullet" || damage_flag == "laser")
|
||||
empulse(B.loc, 1, 3) //less than screen range, so you can stand out of range to avoid it
|
||||
empulse_using_range(B.loc, 5) //less than screen range, so you can stand out of range to avoid it
|
||||
|
||||
/datum/reagent/blob/electromagnetic_web
|
||||
name = "Electromagnetic Web"
|
||||
@@ -28,6 +28,6 @@
|
||||
/datum/reagent/blob/electromagnetic_web/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
|
||||
reac_volume = ..()
|
||||
if(prob(reac_volume*2))
|
||||
M.emp_act(EMP_LIGHT)
|
||||
M.emp_act(50)
|
||||
if(M)
|
||||
M.apply_damage(reac_volume, BURN, wound_bonus=CANT_WOUND)
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
if(severity > 0)
|
||||
if(overmind)
|
||||
overmind.blobstrain.emp_reaction(src, severity)
|
||||
if(prob(100 - severity * 30))
|
||||
if(prob(severity/1.5))
|
||||
new /obj/effect/temp_visual/emp(get_turf(src))
|
||||
|
||||
/obj/structure/blob/zap_act(power)
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
if(owner.current.gender == MALE)
|
||||
if(prob(10)) // Gender override
|
||||
bloodsucker_reputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King")
|
||||
else
|
||||
else if(owner.current.gender == FEMALE)
|
||||
if(prob(10)) // Gender override
|
||||
bloodsucker_reputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen")
|
||||
|
||||
@@ -341,10 +341,12 @@
|
||||
|
||||
//This handles the application of antag huds/special abilities
|
||||
/datum/antagonist/bloodsucker/apply_innate_effects(mob/living/mob_override)
|
||||
RegisterSignal(owner.current,COMSIG_LIVING_BIOLOGICAL_LIFE,.proc/LifeTick)
|
||||
return
|
||||
|
||||
//This handles the removal of antag huds/special abilities
|
||||
/datum/antagonist/bloodsucker/remove_innate_effects(mob/living/mob_override)
|
||||
UnregisterSignal(owner.current,COMSIG_LIVING_BIOLOGICAL_LIFE)
|
||||
return
|
||||
|
||||
//Assign default team and creates one for one of a kind team antagonists
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
/datum/action/bloodsucker/targeted/brawn
|
||||
name = "Brawn"//"Cellular Emporium"
|
||||
name = "Brawn"
|
||||
desc = "Snap restraints with ease, or deal terrible damage with your bare hands."
|
||||
button_icon_state = "power_strength"
|
||||
bloodcost = 10
|
||||
@@ -107,8 +107,6 @@
|
||||
if(user_C.handcuffed)
|
||||
var/obj/O = user_C.get_item_by_slot(SLOT_HANDCUFFED)
|
||||
if(istype(O))
|
||||
//user_C.visible_message("<span class='warning'>[user_C] attempts to remove [O]!</span>", \
|
||||
// "<span class='warning'>You snap [O] like it's nothing!</span>")
|
||||
user_C.clear_cuffs(O,TRUE)
|
||||
playsound(get_turf(usr), 'sound/effects/grillehit.ogg', 80, 1, -1)
|
||||
return TRUE
|
||||
@@ -128,8 +126,6 @@
|
||||
if(user_C.legcuffed)
|
||||
var/obj/O = user_C.get_item_by_slot(SLOT_LEGCUFFED)
|
||||
if(istype(O))
|
||||
//user_C.visible_message("<span class='warning'>[user_C] attempts to remove [O]!</span>", \
|
||||
// "<span class='warning'>You snap [O] like it's nothing!</span>")
|
||||
user_C.clear_cuffs(O,TRUE)
|
||||
playsound(get_turf(usr), 'sound/effects/grillehit.ogg', 80, 1, -1)
|
||||
return TRUE
|
||||
|
||||
@@ -165,8 +165,8 @@
|
||||
// Broadcast Message
|
||||
if(amSilent)
|
||||
//if (!iscarbon(target))
|
||||
// user.visible_message("<span class='notice'>[user] shifts [target] closer to [user.p_their()] mouth.</span>", \
|
||||
// "<span class='notice'>You secretly slip your fangs into [target]'s flesh.</span>", \
|
||||
// user.visible_message("<span class='notice'>[user] shifts [target] closer to [user.p_their()] mouth.</span>",
|
||||
// "<span class='notice'>You secretly slip your fangs into [target]'s flesh.</span>",
|
||||
// vision_distance = 2, ignored_mobs=target) // Only people who AREN'T the target will notice this action.
|
||||
//else
|
||||
var/deadmessage = target.stat == DEAD ? "" : " <i>[target.p_they(TRUE)] looks dazed, and will not remember this.</i>"
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
B.decoy_override = FALSE
|
||||
remove_changeling_powers()
|
||||
owner.special_role = null
|
||||
owner.current.hud_used?.lingchemdisplay?.invisibility = INVISIBILITY_ABSTRACT
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/changeling/proc/remove_clownmut()
|
||||
@@ -225,6 +226,8 @@
|
||||
else //not dead? no chem/geneticdamage caps.
|
||||
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage)
|
||||
geneticdamage = max(0, geneticdamage-1)
|
||||
owner.current.hud_used?.lingchemdisplay?.invisibility = 0
|
||||
owner.current.hud_used?.lingchemdisplay?.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#dd66dd'>[round(chem_charges)]</font></div>"
|
||||
|
||||
|
||||
/datum/antagonist/changeling/proc/get_dna(dna_owner)
|
||||
@@ -357,10 +360,12 @@
|
||||
B.organ_flags &= ~ORGAN_VITAL
|
||||
B.decoy_override = TRUE
|
||||
update_changeling_icons_added()
|
||||
RegisterSignal(owner.current,COMSIG_LIVING_BIOLOGICAL_LIFE,.proc/regenerate)
|
||||
return
|
||||
|
||||
/datum/antagonist/changeling/remove_innate_effects()
|
||||
update_changeling_icons_removed()
|
||||
UnregisterSignal(owner.current,COMSIG_LIVING_BIOLOGICAL_LIFE)
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -3,41 +3,19 @@
|
||||
desc = "Expels impurifications from our form; curing diseases, removing parasites, sobering us, purging toxins and radiation, and resetting our genetic code completely."
|
||||
helptext = "Can be used while unconscious."
|
||||
chemical_cost = 20
|
||||
dna_cost = 1
|
||||
dna_cost = 2
|
||||
req_stat = UNCONSCIOUS
|
||||
action_icon = 'icons/mob/actions/actions_changeling.dmi'
|
||||
action_icon_state = "ling_anatomic_panacea"
|
||||
action_background_icon_state = "bg_ling"
|
||||
|
||||
//Heals the things that the other regenerative abilities don't.
|
||||
/obj/effect/proc_holder/changeling/panacea/sting_action(mob/user)
|
||||
/obj/effect/proc_holder/changeling/panacea/sting_action(mob/living/user)
|
||||
if(user.has_status_effect(STATUS_EFFECT_PANACEA))
|
||||
to_chat(user, "<span class='warning'>We are already cleansing our impurities!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>We cleanse impurities from our form.</span>")
|
||||
|
||||
var/list/bad_organs = list(
|
||||
user.getorgan(/obj/item/organ/body_egg),
|
||||
user.getorgan(/obj/item/organ/zombie_infection))
|
||||
|
||||
for(var/o in bad_organs)
|
||||
var/obj/item/organ/O = o
|
||||
if(!istype(O))
|
||||
continue
|
||||
|
||||
O.Remove()
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.vomit(0, toxic = TRUE)
|
||||
O.forceMove(get_turf(user))
|
||||
|
||||
user.reagents.add_reagent(/datum/reagent/medicine/mutadone, 10)
|
||||
user.reagents.add_reagent(/datum/reagent/medicine/pen_acid/pen_jelly, 20)
|
||||
user.reagents.add_reagent(/datum/reagent/medicine/antihol, 10)
|
||||
user.reagents.add_reagent(/datum/reagent/medicine/mannitol, 25)
|
||||
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
for(var/thing in L.diseases)
|
||||
var/datum/disease/D = thing
|
||||
if(D.severity == DISEASE_SEVERITY_POSITIVE)
|
||||
continue
|
||||
D.cure()
|
||||
user.apply_status_effect(STATUS_EFFECT_PANACEA)
|
||||
return TRUE
|
||||
|
||||
//buffs.dm has the code for anatomic panacea
|
||||
|
||||
@@ -48,6 +48,6 @@
|
||||
for(var/obj/machinery/light/L in range(5, usr))
|
||||
L.on = 1
|
||||
L.break_light_tube()
|
||||
empulse(get_turf(user), 2, 5, 1)
|
||||
empulse_using_range(get_turf(user), 8, TRUE)
|
||||
playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
|
||||
return TRUE
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
L.flash_act(1, 1)
|
||||
if(issilicon(target))
|
||||
var/mob/living/silicon/S = L
|
||||
S.emp_act(EMP_HEAVY)
|
||||
S.emp_act(80)
|
||||
else //for Nar'sian weaklings
|
||||
to_chat(L, "<span class='heavy_brass'>\"How does it feel to see the light, dog?\"</span>")
|
||||
L.visible_message("<span class='warning'>[L]'s eyes flare with burning light!</span>", \
|
||||
|
||||
@@ -251,7 +251,8 @@
|
||||
var/mob/camera/eminence/E = owner
|
||||
E.eminence_help()
|
||||
|
||||
//Returns to the Ark
|
||||
/*
|
||||
//Returns to the Ark - Commented out and replaced with obelisk_jump
|
||||
/datum/action/innate/eminence/ark_jump
|
||||
name = "Return to Ark"
|
||||
desc = "Warps you to the Ark."
|
||||
@@ -265,6 +266,40 @@
|
||||
flash_color(owner, flash_color = "#AF0AAF", flash_time = 25)
|
||||
else
|
||||
to_chat(owner, "<span class='warning'>There is no Ark!</span>")
|
||||
*/
|
||||
|
||||
//Warps to a chosen Obelisk
|
||||
/datum/action/innate/eminence/obelisk_jump
|
||||
name = "Warp to Obelisk"
|
||||
desc = "Warps to a chosen clockwork obelisk."
|
||||
button_icon_state = "Abscond"
|
||||
|
||||
/datum/action/innate/eminence/obelisk_jump/Activate()
|
||||
var/list/possible_targets = list()
|
||||
var/list/warpnames = list()
|
||||
|
||||
for(var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/O in GLOB.all_clockwork_objects)
|
||||
if(!O.Adjacent(owner) && O.anchored)
|
||||
var/area/A = get_area(O)
|
||||
var/locname = initial(A.name)
|
||||
possible_targets[avoid_assoc_duplicate_keys("[locname] [O.name]", warpnames)] = O
|
||||
|
||||
if(!possible_targets.len)
|
||||
to_chat(owner, "<span class='warning'>There are no Obelisks to warp to!</span>")
|
||||
return
|
||||
|
||||
var/target_key = input(owner, "Choose an Obelisk to warp to.", "Obelisk Warp") as null|anything in possible_targets
|
||||
var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/target = possible_targets[target_key]
|
||||
|
||||
if(!target_key || !owner)
|
||||
return
|
||||
|
||||
if(!target)
|
||||
to_chat(owner, "<span class='warning'>That Obelisk does no longer exist!</span>")
|
||||
return
|
||||
owner.forceMove(get_turf(target))
|
||||
owner.playsound_local(owner, 'sound/magic/magic_missile.ogg', 50, TRUE)
|
||||
flash_color(owner, flash_color = "#AF0AAF", flash_time = 25)
|
||||
|
||||
//Warps to the Station
|
||||
/datum/action/innate/eminence/station_jump
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/obj/structure/destructible/clockwork/trap/power_nullifier/activate()
|
||||
if(!activated)
|
||||
activated = TRUE
|
||||
empulse(get_turf(src),1,1,TRUE)
|
||||
empulse_using_range(get_turf(src),1,TRUE)
|
||||
|
||||
/obj/structure/destructible/clockwork/trap/power_nullifier/emp_act(var/strength=1)
|
||||
activate()
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
/datum/action/innate/cult/blood_spell/emp/Activate()
|
||||
owner.visible_message("<span class='warning'>[owner]'s hand flashes a bright blue!</span>", \
|
||||
"<span class='cultitalic'>You speak the cursed words, emitting an EMP blast from your hand.</span>")
|
||||
empulse(owner, 2, 5)
|
||||
empulse_using_range(owner, 8)
|
||||
owner.whisper(invocation, language = /datum/language/common)
|
||||
charges--
|
||||
if(charges<=0)
|
||||
@@ -451,7 +451,7 @@
|
||||
L.flash_act(1,1)
|
||||
if(issilicon(target))
|
||||
var/mob/living/silicon/S = L
|
||||
S.emp_act(EMP_HEAVY)
|
||||
S.emp_act(80)
|
||||
else if(iscarbon(target))
|
||||
var/mob/living/carbon/C = L
|
||||
C.silent += clamp(12 - C.silent, 0, 6)
|
||||
@@ -610,7 +610,7 @@
|
||||
var/prev_color = candidate.color
|
||||
candidate.color = "black"
|
||||
if(do_after(user, 90, target = candidate))
|
||||
candidate.emp_act(EMP_HEAVY)
|
||||
candidate.emp_act(80)
|
||||
var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
|
||||
user.visible_message("<span class='danger'>The dark cloud receedes from what was formerly [candidate], revealing a\n [construct_class]!</span>")
|
||||
switch(construct_class)
|
||||
|
||||
@@ -142,8 +142,10 @@
|
||||
if(ishuman(current))
|
||||
var/mob/living/carbon/human/H = current
|
||||
var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES)
|
||||
H.eye_color = eyes?.eye_color || initial(H.eye_color)
|
||||
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
|
||||
H.left_eye_color = eyes?.left_eye_color || initial(H.left_eye_color)
|
||||
H.right_eye_color = eyes?.right_eye_color || initial(H.right_eye_color)
|
||||
H.dna?.update_ui_block(DNA_LEFT_EYE_COLOR_BLOCK)
|
||||
H.dna?.update_ui_block(DNA_RIGHT_EYE_COLOR_BLOCK)
|
||||
REMOVE_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
|
||||
H.update_body()
|
||||
H.cut_overlays()
|
||||
@@ -236,8 +238,10 @@
|
||||
|
||||
if(ishuman(current))
|
||||
var/mob/living/carbon/human/H = current
|
||||
H.eye_color = initial(H.eye_color)
|
||||
H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
|
||||
H.left_eye_color = initial(H.left_eye_color)
|
||||
H.right_eye_color = initial(H.right_eye_color)
|
||||
H.dna?.update_ui_block(DNA_LEFT_EYE_COLOR_BLOCK)
|
||||
H.dna?.update_ui_block(DNA_RIGHT_EYE_COLOR_BLOCK)
|
||||
REMOVE_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
|
||||
H.cut_overlays()
|
||||
H.regenerate_icons()
|
||||
@@ -312,8 +316,10 @@
|
||||
/datum/team/cult/proc/rise(cultist)
|
||||
if(ishuman(cultist))
|
||||
var/mob/living/carbon/human/H = cultist
|
||||
H.eye_color = "f00"
|
||||
H.dna?.update_ui_block(DNA_EYE_COLOR_BLOCK)
|
||||
H.left_eye_color = "f00"
|
||||
H.right_eye_color = "f00"
|
||||
H.dna?.update_ui_block(DNA_LEFT_EYE_COLOR_BLOCK)
|
||||
H.dna?.update_ui_block(DNA_RIGHT_EYE_COLOR_BLOCK)
|
||||
ADD_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
|
||||
H.update_body()
|
||||
|
||||
|
||||
@@ -174,7 +174,14 @@
|
||||
new reward(get_turf(src))
|
||||
to_chat(user, "<span class='cultitalic'>You work the forge as dark knowledge guides your hands, creating the [choice]!</span>")
|
||||
|
||||
|
||||
/obj/structure/destructible/cult/forge/attackby(obj/item/I, mob/user)
|
||||
if(!iscultist(user))
|
||||
to_chat(user, "<span class='warning'>The heat radiating from [src] pushes you back.</span>")
|
||||
return
|
||||
if(istype(I, /obj/item/ingot))
|
||||
var/obj/item/ingot/notsword = I
|
||||
to_chat(user, "You heat the [notsword] in the [src].")
|
||||
notsword.workability = "shapeable"
|
||||
|
||||
/obj/structure/destructible/cult/pylon
|
||||
name = "pylon"
|
||||
|
||||
@@ -982,7 +982,7 @@ structure_check() searches for nearby cultist structures required for the invoca
|
||||
visible_message("<span class='warning'>A colossal shockwave of energy bursts from the rune, disintegrating it in the process!</span>")
|
||||
for(var/mob/living/L in range(src, 3))
|
||||
L.DefaultCombatKnockdown(30)
|
||||
empulse(T, 0.42*(intensity), 1)
|
||||
empulse_using_range(T, 0.65*(intensity))
|
||||
var/list/images = list()
|
||||
var/zmatch = T.z
|
||||
var/datum/atom_hud/AH = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
name = "Spawn Sentient Disease"
|
||||
typepath = /datum/round_event/ghost_role/sentient_disease
|
||||
weight = 7
|
||||
gamemode_blacklist = list("dynamic")
|
||||
max_occurrences = 1
|
||||
min_players = 5
|
||||
|
||||
|
||||
@@ -83,17 +83,16 @@ the new instance inside the host to be updated to the template's stats.
|
||||
to_chat(src, "<span class='warning'>You have [DisplayTimeText(freemove_end - world.time)] to select your first host. Click on a human to select your host.</span>")
|
||||
|
||||
|
||||
/mob/camera/disease/Stat()
|
||||
/mob/camera/disease/get_status_tab_items()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(freemove)
|
||||
stat("Host Selection Time: [round((freemove_end - world.time)/10)]s")
|
||||
else
|
||||
stat("Adaptation Points: [points]/[total_points]")
|
||||
stat("Hosts: [disease_instances.len]")
|
||||
var/adapt_ready = next_adaptation_time - world.time
|
||||
if(adapt_ready > 0)
|
||||
stat("Adaptation Ready: [round(adapt_ready/10, 0.1)]s")
|
||||
if(freemove)
|
||||
. += "Host Selection Time: [round((freemove_end - world.time)/10)]s"
|
||||
else
|
||||
. += "Adaptation Points: [points]/[total_points]"
|
||||
. += "Hosts: [disease_instances.len]"
|
||||
var/adapt_ready = next_adaptation_time - world.time
|
||||
if(adapt_ready > 0)
|
||||
. += "Adaptation Ready: [round(adapt_ready/10, 0.1)]s"
|
||||
|
||||
|
||||
/mob/camera/disease/examine(mob/user)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
job_rank = ROLE_HERETIC
|
||||
antag_hud_type = ANTAG_HUD_HERETIC
|
||||
antag_hud_name = "heretic"
|
||||
threat = 10
|
||||
var/give_equipment = TRUE
|
||||
var/list/researched_knowledge = list()
|
||||
var/total_sacrifices = 0
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
/datum/antagonist/heretic/on_gain()
|
||||
var/mob/living/current = owner.current
|
||||
owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex)
|
||||
if(ishuman(current))
|
||||
forge_primary_objectives()
|
||||
gain_knowledge(/datum/eldritch_knowledge/spell/basic)
|
||||
@@ -40,7 +42,6 @@
|
||||
START_PROCESSING(SSprocessing,src)
|
||||
if(give_equipment)
|
||||
equip_cultist()
|
||||
owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/heretic/on_removal()
|
||||
@@ -110,17 +111,12 @@
|
||||
P.find_target(owners,assasination)
|
||||
protection += P.target
|
||||
objectives += P
|
||||
|
||||
|
||||
var/datum/objective/sacrifice_ecult/SE = new
|
||||
SE.owner = owner
|
||||
SE.update_explanation_text()
|
||||
objectives += SE
|
||||
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
objectives += escape_objective
|
||||
|
||||
/datum/antagonist/heretic/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
@@ -208,6 +204,14 @@
|
||||
/datum/antagonist/heretic/proc/get_all_knowledge()
|
||||
return researched_knowledge
|
||||
|
||||
/datum/antagonist/heretic/threat()
|
||||
. = ..()
|
||||
for(var/X in researched_knowledge)
|
||||
var/datum/eldritch_knowledge/EK = researched_knowledge[X]
|
||||
. += EK.cost
|
||||
if(ascended)
|
||||
. += 20
|
||||
|
||||
////////////////
|
||||
// Objectives //
|
||||
////////////////
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
if(!is_in_use)
|
||||
INVOKE_ASYNC(src, .proc/activate , user)
|
||||
|
||||
/obj/effect/eldritch/attacked_by(obj/item/I, mob/living/user)
|
||||
/obj/effect/eldritch/attackby(obj/item/I, mob/living/user)
|
||||
. = ..()
|
||||
if(istype(I,/obj/item/nullrod))
|
||||
if(istype(I, /obj/item/storage/book/bible) || istype(I, /obj/item/nullrod))
|
||||
user.say("BEGONE FOUL MAGICKS!!", forced = "bible")
|
||||
to_chat(user, "<span class='danger'>You disrupt the magic of [src] with [I].</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/eldritch/proc/activate(mob/living/user)
|
||||
|
||||
@@ -16,16 +16,19 @@
|
||||
return
|
||||
var/dist = get_dist(user.loc,target.loc)
|
||||
var/dir = get_dir(user.loc,target.loc)
|
||||
|
||||
switch(dist)
|
||||
if(0 to 15)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is near you. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(16 to 31)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(32 to 127)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!</span>")
|
||||
else
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is beyond our reach.</span>")
|
||||
|
||||
if(user.z != target.z)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is beyond our reach.</span>")
|
||||
else
|
||||
switch(dist)
|
||||
if(0 to 15)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is near you. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(16 to 31)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!</span>")
|
||||
if(32 to 127)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!</span>")
|
||||
else
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is beyond our reach.</span>")
|
||||
|
||||
if(target.stat == DEAD)
|
||||
to_chat(user,"<span class='warning'>[target.real_name] is dead. Bring them onto a transmutation rune!</span>")
|
||||
@@ -86,8 +89,8 @@
|
||||
desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the excruciations it has endured from dead origins."
|
||||
icon_state = "flesh_blade"
|
||||
item_state = "flesh_blade"
|
||||
wound_bonus = 5
|
||||
bare_wound_bonus = 15
|
||||
wound_bonus = 10
|
||||
bare_wound_bonus = 20
|
||||
|
||||
/obj/item/clothing/neck/eldritch_amulet
|
||||
name = "warm eldritch medallion"
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
name = "Break of Dawn"
|
||||
desc = "Starts your journey in the mansus. Allows you to select a target using a living heart on a transmutation rune."
|
||||
gain_text = "Gates of Mansus open up to your mind."
|
||||
next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/spell/silence)
|
||||
cost = 0
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mansus_grasp
|
||||
required_atoms = list(/obj/item/living_heart)
|
||||
@@ -247,7 +247,8 @@
|
||||
if(LH.target && LH.target.stat == DEAD)
|
||||
to_chat(carbon_user,"<span class='danger'>Your patrons accepts your offer...</span>")
|
||||
var/mob/living/carbon/human/H = LH.target
|
||||
H.become_husk()
|
||||
H.become_husk("burn") //Husks the target with removable husking, but causes a bunch of additional burn damage to prevent it from being 'too easy' to do
|
||||
H.adjustFireLoss(200)
|
||||
LH.target = null
|
||||
var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic)
|
||||
|
||||
@@ -301,3 +302,11 @@
|
||||
required_atoms = list(/obj/item/shard,/obj/item/stack/rods)
|
||||
result_atoms = list(/obj/item/melee/sickly_blade)
|
||||
route = "Start"
|
||||
|
||||
/datum/eldritch_knowledge/spell/silence
|
||||
name = "Silence"
|
||||
desc = "Allows you to use the power of the Mansus to force an individual's tongue to be held down for up to twenty seconds. They'll notice quickly, however."
|
||||
gain_text = "They must hold their tongues, for they do not understand."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/trigger/mute/eldritch
|
||||
route = PATH_SIDE
|
||||
|
||||
@@ -43,8 +43,9 @@
|
||||
/obj/item/melee/touch_attack/mansus_fist
|
||||
name = "Mansus Grasp"
|
||||
desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown, major stamina damage aswell as some Brute. It gains additional beneficial effects with certain knowledges you can research."
|
||||
icon_state = "disintegrate"
|
||||
item_state = "disintegrate"
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "mansus_grasp"
|
||||
item_state = "mansus"
|
||||
catchphrase = "T'IESA SIE'KTI VISATA"
|
||||
|
||||
/obj/item/melee/touch_attack/mansus_fist/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
@@ -118,6 +119,7 @@
|
||||
/obj/item/melee/touch_attack/blood_siphon
|
||||
name = "Blood Siphon"
|
||||
desc = "A sinister looking aura that distorts the flow of reality around it."
|
||||
color = RUNE_COLOR_RED
|
||||
icon_state = "disintegrate"
|
||||
item_state = "disintegrate"
|
||||
catchphrase = "SUN'AI'KINI'MAS"
|
||||
@@ -260,41 +262,82 @@
|
||||
/obj/effect/proc_holder/spell/pointed/cleave/long
|
||||
charge_max = 650
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/touch/mad_touch
|
||||
/obj/effect/proc_holder/spell/targeted/touch/mad_touch
|
||||
name = "Touch of Madness"
|
||||
desc = "Touch spell that drains your enemies sanity."
|
||||
school = "transmutation"
|
||||
charge_max = 150
|
||||
desc = "Touch spell that allows you to force the knowledge of the mansus upon your foes."
|
||||
hand_path = /obj/item/melee/touch_attack/mad_touch
|
||||
school = "evocation"
|
||||
charge_max = 1800
|
||||
clothes_req = FALSE
|
||||
invocation_type = "none"
|
||||
range = 2
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mad_touch"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/touch/mad_touch/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!istype(target,/mob/living/carbon/human))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You are unable to touch [target]!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
/obj/item/melee/touch_attack/mad_touch
|
||||
name = "Touch of Madness"
|
||||
desc = "A sinister looking aura that shatters your enemies minds."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "mad_touch"
|
||||
item_state = "madness"
|
||||
catchphrase = "SUNA'IKINTI PROTA"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/touch/mad_touch/cast(list/targets, mob/user)
|
||||
. = ..()
|
||||
for(var/mob/living/carbon/target in targets)
|
||||
if(ishuman(targets))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return
|
||||
if(target.mind && !target.mind.has_antag_datum(/datum/antagonist/heretic))
|
||||
to_chat(user,"<span class='warning'>[target.name] has been cursed!</span>")
|
||||
SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
|
||||
/obj/item/melee/touch_attack/mad_touch/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final
|
||||
if(!proximity_flag || target == user)
|
||||
return
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
|
||||
if(iscarbon(target))
|
||||
playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE)
|
||||
var/mob/living/carbon/C = target
|
||||
C.adjustOrganLoss(ORGAN_SLOT_BRAIN,35)
|
||||
C.DefaultCombatKnockdown(50, override_stamdmg = 0)
|
||||
C.gain_trauma(/datum/brain_trauma/mild/phobia)
|
||||
to_chat(user,"<span class='warning'>[target.name] has been cursed!</span>")
|
||||
SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/touch/grasp_of_decay
|
||||
name = "Grasp of Decay"
|
||||
desc = "A sinister looking touch that rots your foes from the inside out for twenty seconds."
|
||||
hand_path = /obj/item/melee/touch_attack/grasp_of_decay
|
||||
school = "evocation"
|
||||
charge_max = 1200
|
||||
clothes_req = FALSE
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mansus_grasp"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/item/melee/touch_attack/grasp_of_decay
|
||||
name = "Grasp of Decay"
|
||||
desc = "A sinister looking aura that rots your foes from the inside out."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "mansus_grasp"
|
||||
item_state = "mansus"
|
||||
catchphrase = "SKILI'EDUONIS"
|
||||
|
||||
/obj/item/melee/touch_attack/grasp_of_decay/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
|
||||
if(!proximity_flag || target == user)
|
||||
return
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/tar = target
|
||||
if(tar.anti_magic_check())
|
||||
tar.visible_message("<span class='danger'>Spell bounces off of [target]!</span>","<span class='danger'>The spell bounces off of you!</span>")
|
||||
return ..()
|
||||
|
||||
if(iscarbon(target))
|
||||
playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE)
|
||||
var/mob/living/carbon/C = target
|
||||
C.DefaultCombatKnockdown(50, override_stamdmg = 0)
|
||||
C.apply_status_effect(/datum/status_effect/corrosion_curse/lesser)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/nightwatchers_rite
|
||||
name = "Nightwatcher's Rite"
|
||||
desc = "Powerful spell that releases 5 streams of fire away from you."
|
||||
school = "transmutation"
|
||||
@@ -307,7 +350,7 @@
|
||||
action_icon_state = "flames"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final/cast(list/targets, mob/user)
|
||||
/obj/effect/proc_holder/spell/pointed/nightwatchers_rite/cast(list/targets, mob/user)
|
||||
for(var/X in targets)
|
||||
var/T
|
||||
T = line_target(-25, range, X, user)
|
||||
@@ -322,11 +365,12 @@
|
||||
INVOKE_ASYNC(src, .proc/fire_line, user,T)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final/proc/line_target(offset, range, atom/at , atom/user)
|
||||
/obj/effect/proc_holder/spell/pointed/nightwatchers_rite/proc/line_target(offset, range, atom/at , atom/user)
|
||||
if(!at)
|
||||
return
|
||||
var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset
|
||||
var/turf/T = get_turf(user)
|
||||
playsound(user,'sound/magic/fireball.ogg', 200, 1)
|
||||
for(var/i in 1 to range)
|
||||
var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z)
|
||||
if(!check)
|
||||
@@ -334,7 +378,7 @@
|
||||
T = check
|
||||
return (getline(user, T) - get_turf(user))
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/ash_final/proc/fire_line(atom/source, list/turfs)
|
||||
/obj/effect/proc_holder/spell/pointed/nightwatchers_rite/proc/fire_line(atom/source, list/turfs)
|
||||
var/list/hit_list = list()
|
||||
for(var/turf/T in turfs)
|
||||
if(istype(T, /turf/closed))
|
||||
@@ -347,8 +391,8 @@
|
||||
if(L in hit_list || L == source)
|
||||
continue
|
||||
hit_list += L
|
||||
L.adjustFireLoss(20)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by [source]'s fire breath!</span>")
|
||||
L.adjustFireLoss(15)
|
||||
to_chat(L, "<span class='userdanger'>You're hit by a blast of fire!</span>")
|
||||
|
||||
new /obj/effect/hotspot(T)
|
||||
T.hotspot_expose(700,50,1)
|
||||
@@ -368,7 +412,7 @@
|
||||
possible_shapes = list(/mob/living/simple_animal/mouse,\
|
||||
/mob/living/simple_animal/pet/dog/corgi,\
|
||||
/mob/living/simple_animal/hostile/carp,\
|
||||
/mob/living/simple_animal/bot/secbot, \
|
||||
/mob/living/simple_animal/bot/secbot,\
|
||||
/mob/living/simple_animal/pet/fox,\
|
||||
/mob/living/simple_animal/pet/cat )
|
||||
|
||||
@@ -380,8 +424,7 @@
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 300
|
||||
emp_heavy = 6
|
||||
emp_light = 10
|
||||
range = 14
|
||||
sound = 'sound/effects/lingscreech.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/fire_cascade
|
||||
@@ -430,7 +473,7 @@
|
||||
action_background_icon_state = "bg_ecult"
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
charge_max = 700
|
||||
charge_max = 1200
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "fire_ring"
|
||||
///how long it lasts
|
||||
@@ -595,6 +638,39 @@
|
||||
invocation = "AK'LIS"
|
||||
action_background_icon_state = "bg_ecult"
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/trigger/mute/eldritch
|
||||
name = "Silence"
|
||||
desc = "Using the power of the mansus, silences a selected unbeliever for twenty seconds."
|
||||
school = "transmutation"
|
||||
charge_max = 1800
|
||||
clothes_req = FALSE
|
||||
invocation = "VIS'TIEK TAVO'LIZUVIS"
|
||||
invocation_type = "whisper"
|
||||
message = "<span class='userdanger'>It feels as if your tongue is being held down by an unseen force!</span>"
|
||||
starting_spells = list("/obj/effect/proc_holder/spell/targeted/genetic/mute")
|
||||
ranged_mousepointer = 'icons/effects/mouse_pointers/mute_target.dmi'
|
||||
action_background_icon_state = "bg_ecult"
|
||||
action_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
action_icon_state = "mute"
|
||||
active_msg = "You prepare to silence a target..."
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/genetic/mute
|
||||
mutations = list(MUT_MUTE)
|
||||
duration = 200
|
||||
charge_max = 1200 // needs to be higher than the duration or it'll be permanent
|
||||
sound = 'sound/magic/blind.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/pointed/trigger/mute/can_target(atom/target, mob/user, silent)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(!isliving(target))
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='warning'>You can only silence living beings!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/entropic
|
||||
icon = 'icons/effects/160x160.dmi'
|
||||
icon_state = "entropic_plume"
|
||||
|
||||
@@ -95,7 +95,30 @@
|
||||
desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/fiery_rebirth
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/final/ash_final)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/flame_immunity)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/flame_immunity
|
||||
name = "Nightwatcher's Blessing"
|
||||
gain_text = "The True Light will destroy and make something anew of any individual. If only they accepted it."
|
||||
desc = "Becoming one with the ash, you become immune to fire and heat, allowing you to thrive in a more extreme environment.."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/nightwatchers_rite)
|
||||
route = PATH_ASH
|
||||
var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOFIRE)
|
||||
|
||||
/datum/eldritch_knowledge/flame_immunity/on_gain(mob/living/user)
|
||||
to_chat(user, "<span class='warning'>[gain_text]</span>")
|
||||
for(var/X in trait_list)
|
||||
ADD_TRAIT(user,X,MAGIC_TRAIT)
|
||||
|
||||
/datum/eldritch_knowledge/spell/nightwatchers_rite
|
||||
name = "Nightwatcher's Rite"
|
||||
gain_text = "When the Glory of the Lantern scorches and sears their skin, nothing will protect them from the ashes."
|
||||
desc = "Fire off five streams of fire from your hand, each setting ablaze targets hit and scorching them upon contact."
|
||||
cost = 2
|
||||
spell_to_add = /obj/effect/proc_holder/spell/pointed/nightwatchers_rite
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/ash_final)
|
||||
route = PATH_ASH
|
||||
|
||||
/datum/eldritch_knowledge/ash_blade_upgrade
|
||||
@@ -167,7 +190,7 @@
|
||||
required_atoms = list(/mob/living/carbon/human)
|
||||
cost = 5
|
||||
route = PATH_ASH
|
||||
var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE)
|
||||
var/list/trait_list = list(TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE)
|
||||
|
||||
/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc)
|
||||
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
desc = "Empowers your Mansus Grasp to be able to create a single ghoul out of a dead player. You cannot raise the same person twice. Ghouls have only 50 HP and look like husks."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul)
|
||||
var/ghoul_amt = 6
|
||||
var/ghoul_amt = 4
|
||||
var/list/spooky_scaries
|
||||
route = PATH_FLESH
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
cost = 1
|
||||
required_atoms = list(/obj/item/kitchen/knife,/obj/item/reagent_containers/food/snacks/grown/poppy,/obj/item/pen,/obj/item/paper)
|
||||
mob_to_summon = /mob/living/simple_animal/hostile/eldritch/stalker
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/final/flesh_final)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/flesh_blade_upgrade_2)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/summon/ashy
|
||||
@@ -250,3 +250,28 @@
|
||||
carbon_user.gib()
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/eldritch_knowledge/flesh_blade_upgrade_2
|
||||
name = "Remembrance"
|
||||
gain_text = "Pain isn't something easily forgotten."
|
||||
desc = "Your blade remembers more, and remembers how easily bones broke just as its flesh did, guaranteeing dislocated, or broken bones."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/touch_of_madness)
|
||||
route = PATH_FLESH
|
||||
|
||||
/datum/eldritch_knowledge/flesh_blade_upgrade_2/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
|
||||
var/datum/wound/blunt/moderate/moderate_wound = new
|
||||
moderate_wound.apply_wound(bodypart)
|
||||
|
||||
/datum/eldritch_knowledge/spell/touch_of_madness
|
||||
name = "Touch of Madness"
|
||||
gain_text = "The ignorant mind that inhabits their feeble bodies will crumble when they acknowledge - willingly or not, the truth."
|
||||
desc = "By forcing the knowledge of the Mansus upon my foes, I can show them things that would drive any normal man insane."
|
||||
cost = 2
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mad_touch
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/flesh_final)
|
||||
route = PATH_FLESH
|
||||
|
||||
@@ -27,8 +27,13 @@
|
||||
if(E)
|
||||
E.on_effect()
|
||||
H.adjustOrganLoss(pick(ORGAN_SLOT_BRAIN,ORGAN_SLOT_EARS,ORGAN_SLOT_EYES,ORGAN_SLOT_LIVER,ORGAN_SLOT_LUNGS,ORGAN_SLOT_STOMACH,ORGAN_SLOT_HEART),25)
|
||||
else
|
||||
for(var/X in user.mind.spell_list)
|
||||
if(!istype(X,/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp))
|
||||
continue
|
||||
var/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp/MG = X
|
||||
MG.charge_counter = min(round(MG.charge_counter + MG.charge_max * 0.75),MG.charge_max)
|
||||
target.rust_heretic_act()
|
||||
target.emp_act(EMP_HEAVY)
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/spell/area_conversion
|
||||
@@ -43,7 +48,7 @@
|
||||
/datum/eldritch_knowledge/spell/rust_wave
|
||||
name = "Patron's Reach"
|
||||
desc = "You can now send a bolt of rust that corrupts the immediate area, and poisons the first target hit."
|
||||
gain_text = "Messengers of hope fear the rustbringer."
|
||||
gain_text = "Messengers of hope fear the Rustbringer."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/aimed/rust_wave
|
||||
route = PATH_RUST
|
||||
@@ -92,19 +97,20 @@
|
||||
banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters)
|
||||
/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(mob/target,user,proximity_flag,click_parameters)
|
||||
. = ..()
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5)
|
||||
if(!IS_HERETIC(target))
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/carbon_target = target
|
||||
carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5)
|
||||
|
||||
/datum/eldritch_knowledge/spell/entropic_plume
|
||||
name = "Entropic Plume"
|
||||
desc = "You can now send a befuddling plume that blinds, poisons and makes enemies strike each other, while also converting the immediate area into rust."
|
||||
gain_text = "Messengers of hope fear the rustbringer."
|
||||
gain_text = "If they knew, the truth would turn them against eachother."
|
||||
cost = 1
|
||||
spell_to_add = /obj/effect/proc_holder/spell/cone/staggered/entropic_plume
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_fist_upgrade,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/armor
|
||||
@@ -119,12 +125,38 @@
|
||||
/datum/eldritch_knowledge/essence
|
||||
name = "Priest's Ritual"
|
||||
desc = "You can now transmute a tank of water into a bottle of eldritch fluid."
|
||||
gain_text = "This is an old recipe, i got it from an owl."
|
||||
gain_text = "This is an old recipe, I got it from an owl."
|
||||
cost = 1
|
||||
next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/spell/ashen_shift)
|
||||
required_atoms = list(/obj/structure/reagent_dispensers/watertank)
|
||||
result_atoms = list(/obj/item/reagent_containers/glass/beaker/eldritch)
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist_upgrade
|
||||
name = "Vile Grip"
|
||||
desc = "Empowers your Mansus Grasp further, sickening your foes and making them vomit, while also strengthening the rate at which your hand decays objects."
|
||||
gain_text = "A sickly diseased touch that was, yet, so welcoming."
|
||||
cost = 2
|
||||
next_knowledge = list(/datum/eldritch_knowledge/spell/grasp_of_decay)
|
||||
var/rust_force = 750
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava,/turf/open/chasm,/turf/open/floor/plating/rust))
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/rust_fist_upgrade/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
H.set_disgust(75)
|
||||
return TRUE
|
||||
|
||||
/datum/eldritch_knowledge/spell/grasp_of_decay
|
||||
name = "Grasp of Decay"
|
||||
desc = "Applying your knowledge of rust to the human body, a knowledge that could decay your foes from the inside out, resulting in organ failure, vomiting, or eventual death through peeling flesh."
|
||||
gain_text = "Decay, similar to Rust, yet so much more terribly uninviting."
|
||||
cost = 2
|
||||
spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/grasp_of_decay
|
||||
next_knowledge = list(/datum/eldritch_knowledge/final/rust_final)
|
||||
route = PATH_RUST
|
||||
|
||||
/datum/eldritch_knowledge/final/rust_final
|
||||
name = "Rustbringer's Oath"
|
||||
desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall."
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
if(visualsOnly)
|
||||
return
|
||||
H.fully_replace_character_name(null,"Waldo")
|
||||
H.eye_color = "000"
|
||||
H.left_eye_color = "000"
|
||||
H.right_eye_color = "000"
|
||||
H.gender = MALE
|
||||
H.skin_tone = "caucasian3"
|
||||
H.hair_style = "Business Hair 3"
|
||||
|
||||
@@ -130,12 +130,11 @@
|
||||
update_spooky_icon()
|
||||
update_health_hud()
|
||||
|
||||
/mob/living/simple_animal/revenant/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Current essence: [essence]/[essence_regen_cap]E")
|
||||
stat(null, "Stolen essence: [essence_accumulated]E")
|
||||
stat(null, "Stolen perfect souls: [perfectsouls]")
|
||||
/mob/living/simple_animal/revenant/get_status_tab_items()
|
||||
. = ..()
|
||||
. += "Current essence: [essence]/[essence_regen_cap]E"
|
||||
. += "Stolen essence: [essence_accumulated]E"
|
||||
. += "Stolen perfect souls: [perfectsouls]"
|
||||
|
||||
/mob/living/simple_animal/revenant/update_health_hud()
|
||||
if(hud_used)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
ShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["alt"])
|
||||
altclick_listed_turf(A)
|
||||
AltClickNoInteract(src, A)
|
||||
return
|
||||
|
||||
if(ishuman(A))
|
||||
@@ -300,7 +300,7 @@
|
||||
continue
|
||||
to_chat(human, "<span class='revenwarning'>You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")].</span>")
|
||||
new /obj/effect/temp_visual/revenant(human.loc)
|
||||
human.emp_act(EMP_HEAVY)
|
||||
human.emp_act(80)
|
||||
for(var/obj/thing in T)
|
||||
if(istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes)) //Doesn't work on SMES and APCs, to prevent kekkery
|
||||
continue
|
||||
@@ -310,12 +310,12 @@
|
||||
thing.emag_act(null)
|
||||
else
|
||||
if(!istype(thing, /obj/machinery/clonepod)) //I hate everything but mostly the fact there's no better way to do this without just not affecting it at all
|
||||
thing.emp_act(EMP_HEAVY)
|
||||
thing.emp_act(80)
|
||||
for(var/mob/living/silicon/robot/S in T) //Only works on cyborgs, not AI
|
||||
playsound(S, 'sound/machines/warning-buzzer.ogg', 50, 1)
|
||||
new /obj/effect/temp_visual/revenant(S.loc)
|
||||
S.spark_system.start()
|
||||
S.emp_act(EMP_HEAVY)
|
||||
S.emp_act(80)
|
||||
|
||||
//Blight: Infects nearby humans and in general messes living stuff up.
|
||||
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
name = "Spawn Revenant" // Did you mean 'griefghost'?
|
||||
typepath = /datum/round_event/ghost_role/revenant
|
||||
weight = 7
|
||||
gamemode_blacklist = list("dynamic")
|
||||
max_occurrences = 1
|
||||
min_players = 5
|
||||
|
||||
|
||||
@@ -66,6 +66,10 @@
|
||||
var/wound_bonus_per_hit = 5
|
||||
// How much our wound_bonus hitstreak bonus caps at (peak demonry)
|
||||
var/wound_bonus_hitstreak_max = 12
|
||||
// Keep the people we eat
|
||||
var/list/consumed_mobs = list()
|
||||
//buffs only happen when hearts are eaten, so this needs to be kept track separately
|
||||
var/consumed_buff = 0
|
||||
|
||||
/mob/living/simple_animal/slaughter/Initialize()
|
||||
..()
|
||||
@@ -112,8 +116,44 @@
|
||||
/mob/living/simple_animal/slaughter/phasein()
|
||||
. = ..()
|
||||
add_movespeed_modifier(/datum/movespeed_modifier/slaughter)
|
||||
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, /datum/movespeed_modifier/slaughter), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
|
||||
var/slowdown_time = 6 SECONDS + (0.5 * consumed_buff)
|
||||
addtimer(CALLBACK(src, .proc/remove_movespeed_modifier, /datum/movespeed_modifier/slaughter), slowdown_time, TIMER_UNIQUE | TIMER_OVERRIDE)
|
||||
|
||||
/mob/living/simple_animal/slaughter/Destroy()
|
||||
release_victims()
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/slaughter/proc/release_victims()
|
||||
if(!consumed_mobs)
|
||||
return
|
||||
|
||||
for(var/mob/living/M in consumed_mobs)
|
||||
if(!M)
|
||||
continue
|
||||
var/turf/T = find_safe_turf()
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
M.forceMove(T)
|
||||
|
||||
/mob/living/simple_animal/slaughter/proc/refresh_consumed_buff()
|
||||
melee_damage_lower = 22.5 + (0.5 * consumed_buff)
|
||||
melee_damage_upper = 22.5 + (1 * consumed_buff)
|
||||
|
||||
/mob/living/simple_animal/slaughter/bloodcrawl_swallow(var/mob/living/victim)
|
||||
if(consumed_mobs)
|
||||
// Keep their corpse so rescue is possible
|
||||
consumed_mobs += victim
|
||||
victim.reagents?.add_reagent(/datum/reagent/preservahyde,3) // make it so that they don't decay in there
|
||||
var/obj/item/organ/heart/heart = victim.getorganslot(ORGAN_SLOT_HEART)
|
||||
if(heart)
|
||||
qdel(heart)
|
||||
consumed_buff++
|
||||
refresh_consumed_buff()
|
||||
else
|
||||
// Be safe and just eject the corpse
|
||||
victim.forceMove(get_turf(victim))
|
||||
victim.exit_blood_effect()
|
||||
victim.visible_message("[victim] falls out of the air, covered in blood, looking highly confused. And dead.")
|
||||
|
||||
//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl
|
||||
/obj/item/organ/heart/demon
|
||||
@@ -178,9 +218,6 @@
|
||||
prison of hugs."
|
||||
loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"})
|
||||
|
||||
// Keep the people we hug!
|
||||
var/list/consumed_mobs = list()
|
||||
|
||||
playstyle_string = "<span class='big bold'>You are a laughter \
|
||||
demon,</span><B> a wonderful creature from another realm. You have a single \
|
||||
desire: <span class='clown'>To hug and tickle.</span><BR>\
|
||||
@@ -195,10 +232,6 @@
|
||||
released and fully healed, because in the end it's just a jape, \
|
||||
sibling!</B>"
|
||||
|
||||
/mob/living/simple_animal/slaughter/laughter/Destroy()
|
||||
release_friends()
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/slaughter/laughter/ex_act(severity)
|
||||
switch(severity)
|
||||
if(1)
|
||||
@@ -208,7 +241,22 @@
|
||||
if(3)
|
||||
adjustBruteLoss(30)
|
||||
|
||||
/mob/living/simple_animal/slaughter/laughter/proc/release_friends()
|
||||
/mob/living/simple_animal/slaughter/laughter/refresh_consumed_buff()
|
||||
melee_damage_lower -= 0.5 // JAPES
|
||||
melee_damage_upper += 1
|
||||
|
||||
/mob/living/simple_animal/slaughter/laughter/bloodcrawl_swallow(var/mob/living/victim)
|
||||
if(consumed_mobs)
|
||||
// Keep their corpse so rescue is possible
|
||||
consumed_mobs += victim
|
||||
refresh_consumed_buff()
|
||||
else
|
||||
// Be safe and just eject the corpse
|
||||
victim.forceMove(get_turf(victim))
|
||||
victim.exit_blood_effect()
|
||||
victim.visible_message("[victim] falls out of the air, covered in blood, looking highly confused. And dead.")
|
||||
|
||||
/mob/living/simple_animal/slaughter/laughter/release_victims()
|
||||
if(!consumed_mobs)
|
||||
return
|
||||
|
||||
@@ -223,13 +271,3 @@
|
||||
M.grab_ghost(force = TRUE)
|
||||
playsound(T, feast_sound, 50, 1, -1)
|
||||
to_chat(M, "<span class='clown'>You leave [src]'s warm embrace, and feel ready to take on the world.</span>")
|
||||
|
||||
/mob/living/simple_animal/slaughter/laughter/bloodcrawl_swallow(var/mob/living/victim)
|
||||
if(consumed_mobs)
|
||||
// Keep their corpse so rescue is possible
|
||||
consumed_mobs += victim
|
||||
else
|
||||
// Be safe and just eject the corpse
|
||||
victim.forceMove(get_turf(victim))
|
||||
victim.exit_blood_effect()
|
||||
victim.visible_message("[victim] falls out of the air, covered in blood, looking highly confused. And dead.")
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/swarmer/Initialize()
|
||||
. = ..()
|
||||
verbs -= /mob/living/verb/pulled
|
||||
remove_verb(src, /mob/living/verb/pulled)
|
||||
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
|
||||
diag_hud.add_to_hud(src)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
max_occurrences = 1 //Only once okay fam
|
||||
earliest_start = 30 MINUTES
|
||||
min_players = 35
|
||||
gamemode_blacklist = list("dynamic")
|
||||
|
||||
|
||||
/datum/round_event/spawn_swarmer
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
/datum/traitor_class/ai/on_removal(datum/antagonist/traitor/T)
|
||||
var/mob/living/silicon/ai/A = T.owner.current
|
||||
A.set_zeroth_law("")
|
||||
A.verbs -= /mob/living/silicon/ai/proc/choose_modules
|
||||
remove_verb(A, /mob/living/silicon/ai/proc/choose_modules)
|
||||
A.malf_picker.remove_malf_verbs(A)
|
||||
qdel(A.malf_picker)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/datum/traitor_class/human/freeform
|
||||
name = "Waffle Co Agent"
|
||||
employer = "Waffle Company"
|
||||
weight = 16
|
||||
weight = 0 // should not spawn in unless admins bus something in the traitor panel with setting traitor classes
|
||||
chaos = 0
|
||||
|
||||
/datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/datum/traitor_class/human/subterfuge
|
||||
name = "MI13 Operative"
|
||||
employer = "MI13"
|
||||
weight = 20
|
||||
weight = 36
|
||||
chaos = -5
|
||||
|
||||
/datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T)
|
||||
@@ -23,18 +23,23 @@
|
||||
maroon_objective.find_target()
|
||||
T.add_objective(maroon_objective)
|
||||
else
|
||||
if(prob(15) && !(locate(/datum/objective/download) in T.objectives) && !(T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
|
||||
var/datum/objective/download/download_objective = new
|
||||
download_objective.owner = T.owner
|
||||
download_objective.gen_amount_goal()
|
||||
T.add_objective(download_objective)
|
||||
else if(prob(70)) // cum. not counting download: 40%.
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = T.owner
|
||||
steal_objective.find_target()
|
||||
T.add_objective(steal_objective)
|
||||
else
|
||||
var/datum/objective/sabotage/sabotage_objective = new
|
||||
sabotage_objective.owner = T.owner
|
||||
sabotage_objective.find_target()
|
||||
T.add_objective(sabotage_objective)
|
||||
var/list/weights = list()
|
||||
weights["sabo"] = length(subtypesof(/datum/sabotage_objective))
|
||||
weights["steal"] = length(subtypesof(/datum/objective_item/steal))
|
||||
weights["download"] = !(locate(/datum/objective/download) in T.objectives || (T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
|
||||
switch(pickweight(weights))
|
||||
if("sabo")
|
||||
var/datum/objective/sabotage/sabotage_objective = new
|
||||
sabotage_objective.owner = T.owner
|
||||
sabotage_objective.find_target()
|
||||
T.add_objective(sabotage_objective)
|
||||
if("steal")
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = T.owner
|
||||
steal_objective.find_target()
|
||||
T.add_objective(steal_objective)
|
||||
if("download")
|
||||
var/datum/objective/download/download_objective = new
|
||||
download_objective.owner = T.owner
|
||||
download_objective.gen_amount_goal()
|
||||
T.add_objective(download_objective)
|
||||
|
||||
@@ -13,7 +13,9 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
|
||||
/obj/machinery/syndicatebomb/badmin/clown,
|
||||
/obj/machinery/syndicatebomb/empty,
|
||||
/obj/machinery/syndicatebomb/self_destruct,
|
||||
/obj/machinery/syndicatebomb/training
|
||||
/obj/machinery/syndicatebomb/training,
|
||||
/obj/machinery/gravity_generator,
|
||||
/obj/machinery/gravity_generator/main
|
||||
)))
|
||||
|
||||
//The malf AI action subtype. All malf actions are subtypes of this.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
var/hidden_socks = FALSE
|
||||
|
||||
//Mob procs
|
||||
/mob/living/carbon/human/proc/underwear_toggle()
|
||||
/mob/living/carbon/human/verb/underwear_toggle()
|
||||
set name = "Toggle undergarments"
|
||||
set category = "IC"
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
|
||||
if(!turfing)
|
||||
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
|
||||
G.time_since_last_orgasm = 0
|
||||
G.last_orgasmed = world.time
|
||||
R.clear_reagents()
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //This is used for forced orgasms and other hands-free climaxes
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
var/fluid_efficiency = 1
|
||||
var/fluid_rate = CUM_RATE
|
||||
var/fluid_mult = 1
|
||||
var/time_since_last_orgasm = 500
|
||||
var/last_orgasmed = 0
|
||||
var/aroused_state = FALSE //Boolean used in icon_state strings
|
||||
var/obj/item/organ/genital/linked_organ
|
||||
var/linked_organ_slot //used for linking an apparatus' organ to its other half on update_link().
|
||||
@@ -24,10 +24,6 @@
|
||||
|
||||
/obj/item/organ/genital/Initialize(mapload, do_update = TRUE)
|
||||
. = ..()
|
||||
if(fluid_id)
|
||||
create_reagents(fluid_max_volume, NONE, NO_REAGENTS_VALUE)
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
if(do_update)
|
||||
update()
|
||||
|
||||
@@ -140,8 +136,6 @@
|
||||
/obj/item/organ/genital/proc/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
fluid_max_volume += modifier*2.5
|
||||
fluid_rate += modifier/10
|
||||
if(reagents)
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/proc/update_size()
|
||||
@@ -151,18 +145,14 @@
|
||||
if(!owner || owner.stat == DEAD)
|
||||
aroused_state = FALSE
|
||||
|
||||
/obj/item/organ/genital/on_life()
|
||||
. = ..()
|
||||
if(!reagents || !.)
|
||||
return
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
if(fluid_id && CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
time_since_last_orgasm++
|
||||
|
||||
/obj/item/organ/genital/proc/generate_fluid(datum/reagents/R)
|
||||
var/amount = clamp(fluid_rate * time_since_last_orgasm * fluid_mult,0,fluid_max_volume)
|
||||
var/amount = clamp((fluid_rate * ((world.time - last_orgasmed) / (10 SECONDS)) * fluid_mult),0,fluid_max_volume)
|
||||
R.clear_reagents()
|
||||
R.add_reagent(fluid_id,amount)
|
||||
R.maximum_volume = fluid_max_volume
|
||||
if(fluid_id)
|
||||
R.add_reagent(fluid_id,amount)
|
||||
else if(linked_organ?.fluid_id)
|
||||
R.add_reagent(linked_organ.fluid_id,amount)
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/genital/proc/update_link()
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
Asset cache quick users guide:
|
||||
|
||||
Make a datum in asset_list_items.dm with your assets for your thing.
|
||||
Checkout asset_list.dm for the helper subclasses
|
||||
The simple subclass will most like be of use for most cases.
|
||||
Then call get_asset_datum() with the type of the datum you created and store the return
|
||||
Then call .send(client) on that stored return value.
|
||||
|
||||
Note: If your code uses output() with assets you will need to call asset_flush on the client and wait for it to return before calling output(). You only need do this if .send(client) returned TRUE
|
||||
*/
|
||||
|
||||
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
//This proc sends the asset to the client, but only if it needs it.
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset(client/client, asset_name)
|
||||
return send_asset_list(client, list(asset_name))
|
||||
|
||||
/// Sends a list of assets to a client
|
||||
/// This proc will no longer block, use client.asset_flush() if you to need know when the client has all assets (such as for output()). (This is not required for browse() calls as they use the same message queue as asset sends)
|
||||
/// client - a client or mob
|
||||
/// asset_list - A list of asset filenames to be sent to the client.
|
||||
/// Returns TRUE if any assets were sent.
|
||||
/proc/send_asset_list(client/client, list/asset_list)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
else
|
||||
return
|
||||
else
|
||||
return
|
||||
|
||||
var/list/unreceived = list()
|
||||
|
||||
for (var/asset_name in asset_list)
|
||||
var/datum/asset_cache_item/asset = SSassets.cache[asset_name]
|
||||
if (!asset)
|
||||
continue
|
||||
var/asset_file = asset.resource
|
||||
if (!asset_file)
|
||||
continue
|
||||
|
||||
var/asset_md5 = asset.md5
|
||||
if (client.sent_assets[asset_name] == asset_md5)
|
||||
continue
|
||||
unreceived[asset_name] = asset_md5
|
||||
|
||||
if (unreceived.len)
|
||||
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
to_chat(client, "Sending Resources...")
|
||||
|
||||
for(var/asset in unreceived)
|
||||
var/datum/asset_cache_item/ACI
|
||||
if ((ACI = SSassets.cache[asset]))
|
||||
log_asset("Sending asset [asset] to client [client]")
|
||||
client << browse_rsc(ACI.resource, asset)
|
||||
|
||||
client.sent_assets |= unreceived
|
||||
addtimer(CALLBACK(client, /client/proc/asset_cache_update_json), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
//The proc calls procs that sleep for long times.
|
||||
/proc/getFilesSlow(client/client, list/files, register_asset = TRUE, filerate = 3)
|
||||
var/startingfilerate = filerate
|
||||
for(var/file in files)
|
||||
if (!client)
|
||||
break
|
||||
if (register_asset)
|
||||
register_asset(file, files[file])
|
||||
|
||||
if (send_asset(client, file))
|
||||
if (!(--filerate))
|
||||
filerate = startingfilerate
|
||||
client.asset_flush()
|
||||
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
|
||||
//icons and virtual assets get copied to the dyn rsc before use
|
||||
/proc/register_asset(asset_name, asset)
|
||||
var/datum/asset_cache_item/ACI = new(asset_name, asset)
|
||||
|
||||
//this is technically never something that was supported and i want metrics on how often it happens if at all.
|
||||
if (SSassets.cache[asset_name])
|
||||
var/datum/asset_cache_item/OACI = SSassets.cache[asset_name]
|
||||
if (OACI.md5 != ACI.md5)
|
||||
stack_trace("ERROR: new asset added to the asset cache with the same name as another asset: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]")
|
||||
else
|
||||
var/list/stacktrace = gib_stack_trace()
|
||||
log_asset("WARNING: dupe asset added to the asset cache: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]\n[stacktrace.Join("\n")]")
|
||||
SSassets.cache[asset_name] = ACI
|
||||
return ACI
|
||||
|
||||
/// Returns the url of the asset, currently this is just its name, here to allow further work cdn'ing assets.
|
||||
/// Can be given an asset as well, this is just a work around for buggy edge cases where two assets may have the same name, doesn't matter now, but it will when the cdn comes.
|
||||
/proc/get_asset_url(asset_name, asset = null)
|
||||
var/datum/asset_cache_item/ACI = SSassets.cache[asset_name]
|
||||
return ACI?.url
|
||||
|
||||
//Generated names do not include file extention.
|
||||
//Used mainly for code that deals with assets in a generic way
|
||||
//The same asset will always lead to the same asset name
|
||||
/proc/generate_asset_name(file)
|
||||
return "asset.[md5(fcopy_rsc(file))]"
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
|
||||
/// Process asset cache client topic calls for "asset_cache_preload_data=[HTML+JSON_STRING]
|
||||
/client/proc/asset_cache_preload_data(data)
|
||||
/*var/jsonend = findtextEx(data, "{{{ENDJSONDATA}}}")
|
||||
if (!jsonend)
|
||||
CRASH("invalid asset_cache_preload_data, no jsonendmarker")*/
|
||||
//var/json = html_decode(copytext(data, 1, jsonend))
|
||||
var/json = data
|
||||
var/list/preloaded_assets = json_decode(json)
|
||||
|
||||
@@ -26,19 +22,17 @@
|
||||
sent_assets |= preloaded_assets
|
||||
|
||||
|
||||
/// Updates the client side stored html/json combo file used to keep track of what assets the client has between restarts/reconnects.
|
||||
/client/proc/asset_cache_update_json(verify = FALSE, list/new_assets = list())
|
||||
/// Updates the client side stored json file used to keep track of what assets the client has between restarts/reconnects.
|
||||
/client/proc/asset_cache_update_json()
|
||||
if (world.time - connection_time < 10 SECONDS) //don't override the existing data file on a new connection
|
||||
return
|
||||
if (!islist(new_assets))
|
||||
new_assets = list("[new_assets]" = md5(SSassets.cache[new_assets]))
|
||||
|
||||
src << browse(json_encode(new_assets|sent_assets), "file=asset_data.json&display=0")
|
||||
src << browse(json_encode(sent_assets), "file=asset_data.json&display=0")
|
||||
|
||||
/// Blocks until all currently sending browser assets have been sent.
|
||||
/// Blocks until all currently sending browse and browse_rsc assets have been sent.
|
||||
/// Due to byond limitations, this proc will sleep for 1 client round trip even if the client has no pending asset sends.
|
||||
/// This proc will return an untrue value if it had to return before confirming the send, such as timeout or the client going away.
|
||||
/client/proc/asset_flush(timeout = 50)
|
||||
/client/proc/browse_queue_flush(timeout = 50)
|
||||
var/job = ++last_asset_job
|
||||
var/t = 0
|
||||
var/timeout_time = timeout
|
||||
|
||||
@@ -1,23 +1,41 @@
|
||||
/**
|
||||
* # asset_cache_item
|
||||
*
|
||||
* An internal datum containing info on items in the asset cache. Mainly used to cache md5 info for speed.
|
||||
**/
|
||||
* # asset_cache_item
|
||||
*
|
||||
* An internal datum containing info on items in the asset cache. Mainly used to cache md5 info for speed.
|
||||
*/
|
||||
/datum/asset_cache_item
|
||||
var/name
|
||||
var/url
|
||||
var/md5
|
||||
var/hash
|
||||
var/resource
|
||||
var/ext = ""
|
||||
/// Should this file also be sent via the legacy browse_rsc system
|
||||
/// when cdn transports are enabled?
|
||||
var/legacy = FALSE
|
||||
/// Used by the cdn system to keep legacy css assets with their parent
|
||||
/// css file. (css files resolve urls relative to the css file, so the
|
||||
/// legacy system can't be used if the css file itself could go out over
|
||||
/// the cdn)
|
||||
var/namespace = null
|
||||
/// True if this is the parent css or html file for an asset's namespace
|
||||
var/namespace_parent = FALSE
|
||||
/// TRUE for keeping local asset names when browse_rsc backend is used
|
||||
var/keep_local_name = FALSE
|
||||
|
||||
/datum/asset_cache_item/New(name, file)
|
||||
if (!isfile(file))
|
||||
file = fcopy_rsc(file)
|
||||
md5 = md5(file)
|
||||
if (!md5)
|
||||
md5 = md5(fcopy_rsc(file))
|
||||
if (!md5)
|
||||
CRASH("invalid asset sent to asset cache")
|
||||
debug_world_log("asset cache unexpected success of second fcopy_rsc")
|
||||
|
||||
hash = md5asfile(file) //icons sent to the rsc sometimes md5 incorrectly
|
||||
if (!hash)
|
||||
CRASH("invalid asset sent to asset cache")
|
||||
src.name = name
|
||||
url = name
|
||||
var/extstart = findlasttext(name, ".")
|
||||
if (extstart)
|
||||
ext = ".[copytext(name, extstart+1)]"
|
||||
resource = file
|
||||
|
||||
/datum/asset_cache_item/vv_edit_var(var_name, var_value)
|
||||
return FALSE
|
||||
|
||||
/datum/asset_cache_item/CanProcCall(procname)
|
||||
return FALSE
|
||||
|
||||
@@ -26,25 +26,38 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
return
|
||||
|
||||
|
||||
//If you don't need anything complicated.
|
||||
/// If you don't need anything complicated.
|
||||
/datum/asset/simple
|
||||
_abstract = /datum/asset/simple
|
||||
/// list of assets for this datum in the form of:
|
||||
/// asset_filename = asset_file. At runtime the asset_file will be
|
||||
/// converted into a asset_cache datum.
|
||||
var/assets = list()
|
||||
/// Set to true to have this asset also be sent via the legacy browse_rsc
|
||||
/// system when cdn transports are enabled?
|
||||
var/legacy = FALSE
|
||||
/// TRUE for keeping local asset names when browse_rsc backend is used
|
||||
var/keep_local_name = FALSE
|
||||
|
||||
/datum/asset/simple/register()
|
||||
for(var/asset_name in assets)
|
||||
assets[asset_name] = register_asset(asset_name, assets[asset_name])
|
||||
var/datum/asset_cache_item/ACI = SSassets.transport.register_asset(asset_name, assets[asset_name])
|
||||
if (!ACI)
|
||||
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
|
||||
continue
|
||||
if (legacy)
|
||||
ACI.legacy = legacy
|
||||
if (keep_local_name)
|
||||
ACI.keep_local_name = keep_local_name
|
||||
assets[asset_name] = ACI
|
||||
|
||||
/datum/asset/simple/send(client)
|
||||
. = send_asset_list(client, assets)
|
||||
. = SSassets.transport.send_assets(client, assets)
|
||||
|
||||
/datum/asset/simple/get_url_mappings()
|
||||
. = list()
|
||||
for (var/asset_name in assets)
|
||||
var/datum/asset_cache_item/ACI = assets[asset_name]
|
||||
if (!ACI)
|
||||
continue
|
||||
.[asset_name] = ACI.url
|
||||
.[asset_name] = SSassets.transport.get_asset_url(asset_name, assets[asset_name])
|
||||
|
||||
|
||||
// For registering or sending multiple others at once
|
||||
@@ -88,12 +101,12 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
ensure_stripped()
|
||||
for(var/size_id in sizes)
|
||||
var/size = sizes[size_id]
|
||||
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
|
||||
SSassets.transport.register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
|
||||
var/res_name = "spritesheet_[name].css"
|
||||
var/fname = "data/spritesheets/[res_name]"
|
||||
fdel(fname)
|
||||
text2file(generate_css(), fname)
|
||||
register_asset(res_name, fcopy_rsc(fname))
|
||||
SSassets.transport.register_asset(res_name, fcopy_rsc(fname))
|
||||
fdel(fname)
|
||||
|
||||
/datum/asset/spritesheet/send(client/C)
|
||||
@@ -102,14 +115,14 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
var/all = list("spritesheet_[name].css")
|
||||
for(var/size_id in sizes)
|
||||
all += "[name]_[size_id].png"
|
||||
. = send_asset_list(C, all)
|
||||
. = SSassets.transport.send_assets(C, all)
|
||||
|
||||
/datum/asset/spritesheet/get_url_mappings()
|
||||
if (!name)
|
||||
return
|
||||
. = list("spritesheet_[name].css" = get_asset_url("spritesheet_[name].css"))
|
||||
. = list("spritesheet_[name].css" = SSassets.transport.get_asset_url("spritesheet_[name].css"))
|
||||
for(var/size_id in sizes)
|
||||
.["[name]_[size_id].png"] = get_asset_url("[name]_[size_id].png")
|
||||
.["[name]_[size_id].png"] = SSassets.transport.get_asset_url("[name]_[size_id].png")
|
||||
|
||||
|
||||
|
||||
@@ -134,7 +147,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
for (var/size_id in sizes)
|
||||
var/size = sizes[size_id]
|
||||
var/icon/tiny = size[SPRSZ_ICON]
|
||||
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[get_asset_url("[name]_[size_id].png")]') no-repeat;}"
|
||||
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[SSassets.transport.get_asset_url("[name]_[size_id].png")]') no-repeat;}"
|
||||
|
||||
for (var/sprite_id in sprites)
|
||||
var/sprite = sprites[sprite_id]
|
||||
@@ -188,7 +201,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
return {"<link rel="stylesheet" href="[css_filename()]" />"}
|
||||
|
||||
/datum/asset/spritesheet/proc/css_filename()
|
||||
return get_asset_url("spritesheet_[name].css")
|
||||
return SSassets.transport.get_asset_url("spritesheet_[name].css")
|
||||
|
||||
/datum/asset/spritesheet/proc/icon_tag(sprite_name)
|
||||
var/sprite = sprites[sprite_name]
|
||||
@@ -243,7 +256,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
if (generic_icon_names)
|
||||
asset_name = "[generate_asset_name(asset)].png"
|
||||
|
||||
register_asset(asset_name, asset)
|
||||
SSassets.transport.register_asset(asset_name, asset)
|
||||
|
||||
/datum/asset/simple/icon_states/multiple_icons
|
||||
_abstract = /datum/asset/simple/icon_states/multiple_icons
|
||||
@@ -253,4 +266,52 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
for(var/i in icons)
|
||||
..(i)
|
||||
|
||||
/// Namespace'ed assets (for static css and html files)
|
||||
/// When sent over a cdn transport, all assets in the same asset datum will exist in the same folder, as their plain names.
|
||||
/// Used to ensure css files can reference files by url() without having to generate the css at runtime, both the css file and the files it depends on must exist in the same namespace asset datum. (Also works for html)
|
||||
/// For example `blah.css` with asset `blah.png` will get loaded as `namespaces/a3d..14f/f12..d3c.css` and `namespaces/a3d..14f/blah.png`. allowing the css file to load `blah.png` by a relative url rather then compute the generated url with get_url_mappings().
|
||||
/// The namespace folder's name will change if any of the assets change. (excluding parent assets)
|
||||
/datum/asset/simple/namespaced
|
||||
_abstract = /datum/asset/simple/namespaced
|
||||
/// parents - list of the parent asset or assets (in name = file assoicated format) for this namespace.
|
||||
/// parent assets must be referenced by their generated url, but if an update changes a parent asset, it won't change the namespace's identity.
|
||||
var/list/parents = list()
|
||||
|
||||
/datum/asset/simple/namespaced/register()
|
||||
if (legacy)
|
||||
assets |= parents
|
||||
var/list/hashlist = list()
|
||||
var/list/sorted_assets = sortList(assets)
|
||||
|
||||
for (var/asset_name in sorted_assets)
|
||||
var/datum/asset_cache_item/ACI = new(asset_name, sorted_assets[asset_name])
|
||||
if (!ACI?.hash)
|
||||
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
|
||||
continue
|
||||
hashlist += ACI.hash
|
||||
sorted_assets[asset_name] = ACI
|
||||
var/namespace = md5(hashlist.Join())
|
||||
|
||||
for (var/asset_name in parents)
|
||||
var/datum/asset_cache_item/ACI = new(asset_name, parents[asset_name])
|
||||
if (!ACI?.hash)
|
||||
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
|
||||
continue
|
||||
ACI.namespace_parent = TRUE
|
||||
sorted_assets[asset_name] = ACI
|
||||
|
||||
for (var/asset_name in sorted_assets)
|
||||
var/datum/asset_cache_item/ACI = sorted_assets[asset_name]
|
||||
if (!ACI?.hash)
|
||||
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
|
||||
continue
|
||||
ACI.namespace = namespace
|
||||
|
||||
assets = sorted_assets
|
||||
..()
|
||||
|
||||
/// Get a html string that will load a html asset.
|
||||
/// Needed because byond doesn't allow you to browse() to a url.
|
||||
/datum/asset/simple/namespaced/proc/get_htmlloader(filename)
|
||||
return url2htmlloader(SSassets.transport.get_asset_url(filename, assets[filename]))
|
||||
|
||||
|
||||
@@ -1,81 +1,95 @@
|
||||
//DEFINITIONS FOR ASSET DATUMS START HERE.
|
||||
|
||||
/datum/asset/simple/tgui
|
||||
/datum/asset/simple/tgui_common
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js',
|
||||
"tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css',
|
||||
"tgui-common.chunk.js" = 'tgui/public/tgui-common.chunk.js',
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui.bundle.js" = 'tgui/public/tgui.bundle.js',
|
||||
"tgui.bundle.css" = 'tgui/public/tgui.bundle.css',
|
||||
)
|
||||
|
||||
/datum/asset/simple/tgui_panel
|
||||
keep_local_name = TRUE
|
||||
assets = list(
|
||||
"tgui-panel.bundle.js" = 'tgui/public/tgui-panel.bundle.js',
|
||||
"tgui-panel.bundle.css" = 'tgui/public/tgui-panel.bundle.css',
|
||||
)
|
||||
|
||||
/datum/asset/simple/headers
|
||||
assets = list(
|
||||
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
|
||||
"alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
|
||||
"batt_5.gif" = 'icons/program_icons/batt_5.gif',
|
||||
"batt_20.gif" = 'icons/program_icons/batt_20.gif',
|
||||
"batt_40.gif" = 'icons/program_icons/batt_40.gif',
|
||||
"batt_60.gif" = 'icons/program_icons/batt_60.gif',
|
||||
"batt_80.gif" = 'icons/program_icons/batt_80.gif',
|
||||
"batt_100.gif" = 'icons/program_icons/batt_100.gif',
|
||||
"charging.gif" = 'icons/program_icons/charging.gif',
|
||||
"downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
|
||||
"downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
|
||||
"ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
|
||||
"ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
|
||||
"power_norm.gif" = 'icons/program_icons/power_norm.gif',
|
||||
"power_warn.gif" = 'icons/program_icons/power_warn.gif',
|
||||
"sig_high.gif" = 'icons/program_icons/sig_high.gif',
|
||||
"sig_low.gif" = 'icons/program_icons/sig_low.gif',
|
||||
"sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
|
||||
"sig_none.gif" = 'icons/program_icons/sig_none.gif',
|
||||
"smmon_0.gif" = 'icons/program_icons/smmon_0.gif',
|
||||
"smmon_1.gif" = 'icons/program_icons/smmon_1.gif',
|
||||
"smmon_2.gif" = 'icons/program_icons/smmon_2.gif',
|
||||
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
|
||||
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
|
||||
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
|
||||
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
|
||||
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
|
||||
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
|
||||
"alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
|
||||
"batt_5.gif" = 'icons/program_icons/batt_5.gif',
|
||||
"batt_20.gif" = 'icons/program_icons/batt_20.gif',
|
||||
"batt_40.gif" = 'icons/program_icons/batt_40.gif',
|
||||
"batt_60.gif" = 'icons/program_icons/batt_60.gif',
|
||||
"batt_80.gif" = 'icons/program_icons/batt_80.gif',
|
||||
"batt_100.gif" = 'icons/program_icons/batt_100.gif',
|
||||
"charging.gif" = 'icons/program_icons/charging.gif',
|
||||
"downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
|
||||
"downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
|
||||
"ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
|
||||
"ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
|
||||
"power_norm.gif" = 'icons/program_icons/power_norm.gif',
|
||||
"power_warn.gif" = 'icons/program_icons/power_warn.gif',
|
||||
"sig_high.gif" = 'icons/program_icons/sig_high.gif',
|
||||
"sig_low.gif" = 'icons/program_icons/sig_low.gif',
|
||||
"sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
|
||||
"sig_none.gif" = 'icons/program_icons/sig_none.gif',
|
||||
"smmon_0.gif" = 'icons/program_icons/smmon_0.gif',
|
||||
"smmon_1.gif" = 'icons/program_icons/smmon_1.gif',
|
||||
"smmon_2.gif" = 'icons/program_icons/smmon_2.gif',
|
||||
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
|
||||
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
|
||||
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
|
||||
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
|
||||
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
|
||||
)
|
||||
|
||||
/datum/asset/simple/radar_assets
|
||||
assets = list(
|
||||
"ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
|
||||
"ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
|
||||
"ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
|
||||
"ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
|
||||
"ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
|
||||
"ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
|
||||
)
|
||||
|
||||
/datum/asset/spritesheet/simple/pda
|
||||
name = "pda"
|
||||
assets = list(
|
||||
"atmos" = 'icons/pda_icons/pda_atmos.png',
|
||||
"back" = 'icons/pda_icons/pda_back.png',
|
||||
"bell" = 'icons/pda_icons/pda_bell.png',
|
||||
"blank" = 'icons/pda_icons/pda_blank.png',
|
||||
"boom" = 'icons/pda_icons/pda_boom.png',
|
||||
"bucket" = 'icons/pda_icons/pda_bucket.png',
|
||||
"medbot" = 'icons/pda_icons/pda_medbot.png',
|
||||
"floorbot" = 'icons/pda_icons/pda_floorbot.png',
|
||||
"cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
|
||||
"crate" = 'icons/pda_icons/pda_crate.png',
|
||||
"cuffs" = 'icons/pda_icons/pda_cuffs.png',
|
||||
"eject" = 'icons/pda_icons/pda_eject.png',
|
||||
"flashlight" = 'icons/pda_icons/pda_flashlight.png',
|
||||
"honk" = 'icons/pda_icons/pda_honk.png',
|
||||
"mail" = 'icons/pda_icons/pda_mail.png',
|
||||
"medical" = 'icons/pda_icons/pda_medical.png',
|
||||
"menu" = 'icons/pda_icons/pda_menu.png',
|
||||
"mule" = 'icons/pda_icons/pda_mule.png',
|
||||
"notes" = 'icons/pda_icons/pda_notes.png',
|
||||
"power" = 'icons/pda_icons/pda_power.png',
|
||||
"rdoor" = 'icons/pda_icons/pda_rdoor.png',
|
||||
"reagent" = 'icons/pda_icons/pda_reagent.png',
|
||||
"refresh" = 'icons/pda_icons/pda_refresh.png',
|
||||
"scanner" = 'icons/pda_icons/pda_scanner.png',
|
||||
"signaler" = 'icons/pda_icons/pda_signaler.png',
|
||||
// "skills" = 'icons/pda_icons/pda_skills.png',
|
||||
"status" = 'icons/pda_icons/pda_status.png',
|
||||
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
|
||||
"emoji" = 'icons/pda_icons/pda_emoji.png'
|
||||
"atmos" = 'icons/pda_icons/pda_atmos.png',
|
||||
"back" = 'icons/pda_icons/pda_back.png',
|
||||
"bell" = 'icons/pda_icons/pda_bell.png',
|
||||
"blank" = 'icons/pda_icons/pda_blank.png',
|
||||
"boom" = 'icons/pda_icons/pda_boom.png',
|
||||
"bucket" = 'icons/pda_icons/pda_bucket.png',
|
||||
"medbot" = 'icons/pda_icons/pda_medbot.png',
|
||||
"floorbot" = 'icons/pda_icons/pda_floorbot.png',
|
||||
"cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
|
||||
"crate" = 'icons/pda_icons/pda_crate.png',
|
||||
"cuffs" = 'icons/pda_icons/pda_cuffs.png',
|
||||
"eject" = 'icons/pda_icons/pda_eject.png',
|
||||
"flashlight" = 'icons/pda_icons/pda_flashlight.png',
|
||||
"honk" = 'icons/pda_icons/pda_honk.png',
|
||||
"mail" = 'icons/pda_icons/pda_mail.png',
|
||||
"medical" = 'icons/pda_icons/pda_medical.png',
|
||||
"menu" = 'icons/pda_icons/pda_menu.png',
|
||||
"mule" = 'icons/pda_icons/pda_mule.png',
|
||||
"notes" = 'icons/pda_icons/pda_notes.png',
|
||||
"power" = 'icons/pda_icons/pda_power.png',
|
||||
"rdoor" = 'icons/pda_icons/pda_rdoor.png',
|
||||
"reagent" = 'icons/pda_icons/pda_reagent.png',
|
||||
"refresh" = 'icons/pda_icons/pda_refresh.png',
|
||||
"scanner" = 'icons/pda_icons/pda_scanner.png',
|
||||
"signaler" = 'icons/pda_icons/pda_signaler.png',
|
||||
// "skills" = 'icons/pda_icons/pda_skills.png',
|
||||
"status" = 'icons/pda_icons/pda_status.png',
|
||||
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
|
||||
"emoji" = 'icons/pda_icons/pda_emoji.png'
|
||||
)
|
||||
|
||||
/datum/asset/spritesheet/simple/paper
|
||||
@@ -91,26 +105,26 @@
|
||||
"stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
|
||||
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
|
||||
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
|
||||
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png'
|
||||
// "stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png'
|
||||
// "stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
|
||||
// "stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
|
||||
// "stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
|
||||
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png',
|
||||
"stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png',
|
||||
"stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
|
||||
"stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
|
||||
"stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
|
||||
)
|
||||
|
||||
|
||||
/datum/asset/simple/IRV
|
||||
/datum/asset/simple/irv
|
||||
assets = list(
|
||||
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
|
||||
)
|
||||
|
||||
/datum/asset/group/IRV
|
||||
/datum/asset/group/irv
|
||||
children = list(
|
||||
/datum/asset/simple/jquery,
|
||||
/datum/asset/simple/IRV
|
||||
/datum/asset/simple/irv
|
||||
)
|
||||
|
||||
/datum/asset/simple/changelog
|
||||
/datum/asset/simple/namespaced/changelog
|
||||
assets = list(
|
||||
"88x31.png" = 'html/88x31.png',
|
||||
"bug-minus.png" = 'html/bug-minus.png',
|
||||
@@ -132,43 +146,30 @@
|
||||
"chrome-wrench.png" = 'html/chrome-wrench.png',
|
||||
"changelog.css" = 'html/changelog.css'
|
||||
)
|
||||
parents = list("changelog.html" = 'html/changelog.html')
|
||||
|
||||
/datum/asset/group/goonchat
|
||||
children = list(
|
||||
/datum/asset/simple/jquery,
|
||||
/datum/asset/simple/goonchat,
|
||||
/datum/asset/spritesheet/goonchat,
|
||||
/datum/asset/simple/fontawesome
|
||||
)
|
||||
|
||||
/datum/asset/simple/jquery
|
||||
legacy = TRUE
|
||||
assets = list(
|
||||
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
|
||||
"jquery.min.js" = 'html/jquery.min.js',
|
||||
)
|
||||
|
||||
/datum/asset/simple/goonchat
|
||||
assets = list(
|
||||
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
|
||||
"browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
|
||||
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
|
||||
"browserOutput_dark.css" = 'code/modules/goonchat/browserassets/css/browserOutput_dark.css',
|
||||
"browserOutput_light.css" = 'code/modules/goonchat/browserassets/css/browserOutput_light.css'
|
||||
)
|
||||
|
||||
/datum/asset/simple/fontawesome
|
||||
/datum/asset/simple/namespaced/fontawesome
|
||||
legacy = TRUE
|
||||
assets = list(
|
||||
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
|
||||
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
|
||||
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
|
||||
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
|
||||
"font-awesome.css" = 'html/font-awesome/css/all.min.css',
|
||||
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
|
||||
)
|
||||
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')
|
||||
|
||||
/datum/asset/spritesheet/goonchat
|
||||
/datum/asset/spritesheet/chat
|
||||
name = "chat"
|
||||
|
||||
/datum/asset/spritesheet/goonchat/register()
|
||||
/datum/asset/spritesheet/chat/register()
|
||||
InsertAll("emoji", 'icons/emoji.dmi')
|
||||
InsertAll("emoji", 'icons/emoji_32.dmi')
|
||||
|
||||
@@ -181,12 +182,27 @@
|
||||
if (icon != 'icons/misc/language.dmi')
|
||||
var/icon_state = initial(L.icon_state)
|
||||
Insert("language-[icon_state]", icon, icon_state=icon_state)
|
||||
|
||||
..()
|
||||
|
||||
/datum/asset/simple/lobby
|
||||
assets = list(
|
||||
"playeroptions.css" = 'html/browser/playeroptions.css'
|
||||
)
|
||||
|
||||
/datum/asset/simple/namespaced/common
|
||||
assets = list("padlock.png" = 'html/padlock.png')
|
||||
parents = list("common.css" = 'html/browser/common.css')
|
||||
|
||||
/datum/asset/simple/permissions
|
||||
assets = list(
|
||||
"padlock.png" = 'html/padlock.png'
|
||||
"search.js" = 'html/admin/search.js',
|
||||
"panels.css" = 'html/admin/panels.css'
|
||||
)
|
||||
|
||||
/datum/asset/group/permissions
|
||||
children = list(
|
||||
/datum/asset/simple/permissions,
|
||||
/datum/asset/simple/namespaced/common
|
||||
)
|
||||
|
||||
/datum/asset/simple/notes
|
||||
@@ -206,6 +222,52 @@
|
||||
"boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
|
||||
"boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
|
||||
)
|
||||
/*
|
||||
/datum/asset/spritesheet/simple/achievements
|
||||
name ="achievements"
|
||||
assets = list(
|
||||
"default" = 'icons/UI_Icons/Achievements/default.png',
|
||||
"basemisc" = 'icons/UI_Icons/Achievements/basemisc.png',
|
||||
"baseboss" = 'icons/UI_Icons/Achievements/baseboss.png',
|
||||
"baseskill" = 'icons/UI_Icons/Achievements/baseskill.png',
|
||||
"bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
|
||||
"colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
|
||||
"hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
|
||||
"legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
|
||||
"miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
|
||||
"swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
|
||||
"tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png',
|
||||
"featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png',
|
||||
"helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png',
|
||||
"jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png',
|
||||
"meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png',
|
||||
"timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png',
|
||||
"upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png',
|
||||
"clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
|
||||
"clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
|
||||
"rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
|
||||
"snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
|
||||
"ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
|
||||
"mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
|
||||
"assistant" = 'icons/UI_Icons/Achievements/Mafia/assistant.png',
|
||||
"changeling" = 'icons/UI_Icons/Achievements/Mafia/changeling.png',
|
||||
"chaplain" = 'icons/UI_Icons/Achievements/Mafia/chaplain.png',
|
||||
"clown" = 'icons/UI_Icons/Achievements/Mafia/clown.png',
|
||||
"detective" = 'icons/UI_Icons/Achievements/Mafia/detective.png',
|
||||
"fugitive" = 'icons/UI_Icons/Achievements/Mafia/fugitive.png',
|
||||
"hated" = 'icons/UI_Icons/Achievements/Mafia/hated.png',
|
||||
"hop" = 'icons/UI_Icons/Achievements/Mafia/hop.png',
|
||||
"lawyer" = 'icons/UI_Icons/Achievements/Mafia/lawyer.png',
|
||||
"md" = 'icons/UI_Icons/Achievements/Mafia/md.png',
|
||||
"nightmare" = 'icons/UI_Icons/Achievements/Mafia/nightmare.png',
|
||||
"obsessed" = 'icons/UI_Icons/Achievements/Mafia/obsessed.png',
|
||||
"psychologist" = 'icons/UI_Icons/Achievements/Mafia/psychologist.png',
|
||||
"thoughtfeeder" = 'icons/UI_Icons/Achievements/Mafia/thoughtfeeder.png',
|
||||
"traitor" = 'icons/UI_Icons/Achievements/Mafia/traitor.png',
|
||||
"basemafia" ='icons/UI_Icons/Achievements/basemafia.png',
|
||||
"frenching" = 'icons/UI_Icons/Achievements/Misc/frenchingthebubble.png'
|
||||
)
|
||||
*/
|
||||
|
||||
/datum/asset/spritesheet/simple/minesweeper
|
||||
name = "minesweeper"
|
||||
@@ -225,7 +287,6 @@
|
||||
"minehit" = 'icons/UI_Icons/minesweeper_tiles/minehit.png'
|
||||
)
|
||||
|
||||
|
||||
/datum/asset/spritesheet/simple/pills
|
||||
name ="pills"
|
||||
assets = list(
|
||||
@@ -363,9 +424,9 @@
|
||||
|
||||
/datum/asset/simple/genetics
|
||||
assets = list(
|
||||
"dna_discovered.gif" = 'html/dna_discovered.gif',
|
||||
"dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
|
||||
"dna_extra.gif" = 'html/dna_extra.gif'
|
||||
"dna_discovered.gif" = 'html/dna_discovered.gif',
|
||||
"dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
|
||||
"dna_extra.gif" = 'html/dna_extra.gif'
|
||||
)
|
||||
|
||||
/datum/asset/simple/orbit
|
||||
@@ -388,11 +449,9 @@
|
||||
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
|
||||
..()
|
||||
|
||||
|
||||
/datum/asset/spritesheet/mafia
|
||||
name = "mafia"
|
||||
|
||||
/datum/asset/spritesheet/mafia/register()
|
||||
InsertAll("", 'icons/obj/mafia.dmi')
|
||||
..()
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Asset cache system
|
||||
|
||||
## Framework for managing browser assets (javascript,css,images,etc)
|
||||
|
||||
This manages getting the asset to the client without doing unneeded re-sends, as well as utilizing any configured cdns.
|
||||
|
||||
There are two frameworks for using this system:
|
||||
|
||||
### Asset datum:
|
||||
|
||||
Make a datum in asset_list_items.dm with your browser assets for your thing.
|
||||
|
||||
Checkout asset_list.dm for the helper subclasses
|
||||
|
||||
The `simple` subclass will most likely be of use for most cases.
|
||||
|
||||
Call get_asset_datum() with the type of the datum you created to get your asset cache datum
|
||||
|
||||
Call .send(client|usr) on that datum to send the asset to the client. Depending on the asset transport this may or may not block.
|
||||
|
||||
Call .get_url_mappings() to get an associated list with the urls your assets can be found at.
|
||||
|
||||
### Manual backend:
|
||||
|
||||
See the documentation for `/datum/asset_transport` for the backend api the asset datums utilize.
|
||||
|
||||
The global variable `SSassets.transport` contains the currently configured transport.
|
||||
|
||||
|
||||
|
||||
### Notes:
|
||||
|
||||
Because byond browse() calls use non-blocking queues, if your code uses output() (which bypasses all of these queues) to invoke javascript functions you will need to first have the javascript announce to the server it has loaded before trying to invoke js functions.
|
||||
|
||||
To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
|
||||
|
||||
CSS files that use url() can be made to use the CDN without needing to rewrite all url() calls in code by using the namespaced helper datum. See the documentation for `/datum/asset/simple/namespaced` for details.
|
||||
@@ -0,0 +1,154 @@
|
||||
/// When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
/// Base browse_rsc asset transport
|
||||
/datum/asset_transport
|
||||
var/name = "Simple browse_rsc asset transport"
|
||||
var/static/list/preload
|
||||
/// Don't mutate the filename of assets when sending via browse_rsc.
|
||||
/// This is to make it easier to debug issues with assets, and allow server operators to bypass issues that make it to production.
|
||||
/// If turning this on fixes asset issues, something isn't using get_asset_url and the asset isn't marked legacy, fix one of those.
|
||||
var/dont_mutate_filenames = FALSE
|
||||
|
||||
/// Called when the transport is loaded by the config controller, not called on the default transport unless it gets loaded by a config change.
|
||||
/datum/asset_transport/proc/Load()
|
||||
if (CONFIG_GET(flag/asset_simple_preload))
|
||||
for(var/client/C in GLOB.clients)
|
||||
addtimer(CALLBACK(src, .proc/send_assets_slow, C, preload), 1 SECONDS)
|
||||
|
||||
/// Initialize - Called when SSassets initializes.
|
||||
/datum/asset_transport/proc/Initialize(list/assets)
|
||||
preload = assets.Copy()
|
||||
if (!CONFIG_GET(flag/asset_simple_preload))
|
||||
return
|
||||
for(var/client/C in GLOB.clients)
|
||||
addtimer(CALLBACK(src, .proc/send_assets_slow, C, preload), 1 SECONDS)
|
||||
|
||||
|
||||
/// Register a browser asset with the asset cache system
|
||||
/// asset_name - the identifier of the asset
|
||||
/// asset - the actual asset file (or an asset_cache_item datum)
|
||||
/// returns a /datum/asset_cache_item.
|
||||
/// mutiple calls to register the same asset under the same asset_name return the same datum
|
||||
/datum/asset_transport/proc/register_asset(asset_name, asset)
|
||||
var/datum/asset_cache_item/ACI = asset
|
||||
if (!istype(ACI))
|
||||
ACI = new(asset_name, asset)
|
||||
if (!ACI || !ACI.hash)
|
||||
CRASH("ERROR: Invalid asset: [asset_name]:[asset]:[ACI]")
|
||||
if (SSassets.cache[asset_name])
|
||||
var/datum/asset_cache_item/OACI = SSassets.cache[asset_name]
|
||||
OACI.legacy = ACI.legacy = (ACI.legacy|OACI.legacy)
|
||||
OACI.namespace_parent = ACI.namespace_parent = (ACI.namespace_parent | OACI.namespace_parent)
|
||||
OACI.namespace = OACI.namespace || ACI.namespace
|
||||
if (OACI.hash != ACI.hash)
|
||||
var/error_msg = "ERROR: new asset added to the asset cache with the same name as another asset: [asset_name] existing asset hash: [OACI.hash] new asset hash:[ACI.hash]"
|
||||
stack_trace(error_msg)
|
||||
log_asset(error_msg)
|
||||
else
|
||||
if (length(ACI.namespace))
|
||||
return ACI
|
||||
return OACI
|
||||
|
||||
SSassets.cache[asset_name] = ACI
|
||||
return ACI
|
||||
|
||||
|
||||
/// Returns a url for a given asset.
|
||||
/// asset_name - Name of the asset.
|
||||
/// asset_cache_item - asset cache item datum for the asset, optional, overrides asset_name
|
||||
/datum/asset_transport/proc/get_asset_url(asset_name, datum/asset_cache_item/asset_cache_item)
|
||||
if (!istype(asset_cache_item))
|
||||
asset_cache_item = SSassets.cache[asset_name]
|
||||
// To ensure code that breaks on cdns breaks in local testing, we only
|
||||
// use the normal filename on legacy assets and name space assets.
|
||||
var/keep_local_name = dont_mutate_filenames \
|
||||
|| asset_cache_item.legacy \
|
||||
|| asset_cache_item.keep_local_name \
|
||||
|| (asset_cache_item.namespace && !asset_cache_item.namespace_parent)
|
||||
if (keep_local_name)
|
||||
return url_encode(asset_cache_item.name)
|
||||
return url_encode("asset.[asset_cache_item.hash][asset_cache_item.ext]")
|
||||
|
||||
|
||||
/// Sends a list of browser assets to a client
|
||||
/// client - a client or mob
|
||||
/// asset_list - A list of asset filenames to be sent to the client. Can optionally be assoicated with the asset's asset_cache_item datum.
|
||||
/// Returns TRUE if any assets were sent.
|
||||
/datum/asset_transport/proc/send_assets(client/client, list/asset_list)
|
||||
if (!istype(client))
|
||||
if (ismob(client))
|
||||
var/mob/M = client
|
||||
if (M.client)
|
||||
client = M.client
|
||||
else //no stacktrace because this will mainly happen because the client went away
|
||||
return
|
||||
else
|
||||
CRASH("Invalid argument: client: `[client]`")
|
||||
if (!islist(asset_list))
|
||||
asset_list = list(asset_list)
|
||||
var/list/unreceived = list()
|
||||
|
||||
for (var/asset_name in asset_list)
|
||||
var/datum/asset_cache_item/ACI = asset_list[asset_name]
|
||||
if (!istype(ACI) && !(ACI = SSassets.cache[asset_name]))
|
||||
log_asset("ERROR: can't send asset `[asset_name]`: unregistered or invalid state: `[ACI]`")
|
||||
continue
|
||||
var/asset_file = ACI.resource
|
||||
if (!asset_file)
|
||||
log_asset("ERROR: can't send asset `[asset_name]`: invalid registered resource: `[ACI.resource]`")
|
||||
continue
|
||||
|
||||
var/asset_hash = ACI.hash
|
||||
var/new_asset_name = asset_name
|
||||
var/keep_local_name = dont_mutate_filenames \
|
||||
|| ACI.legacy \
|
||||
|| ACI.keep_local_name \
|
||||
|| (ACI.namespace && !ACI.namespace_parent)
|
||||
if (!keep_local_name)
|
||||
new_asset_name = "asset.[ACI.hash][ACI.ext]"
|
||||
if (client.sent_assets[new_asset_name] == asset_hash)
|
||||
if (GLOB.Debug2)
|
||||
log_asset("DEBUG: Skipping send of `[asset_name]` (as `[new_asset_name]`) for `[client]` because it already exists in the client's sent_assets list")
|
||||
continue
|
||||
unreceived[asset_name] = ACI
|
||||
|
||||
if (unreceived.len)
|
||||
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
to_chat(client, "Sending Resources...")
|
||||
|
||||
for (var/asset_name in unreceived)
|
||||
var/new_asset_name = asset_name
|
||||
var/datum/asset_cache_item/ACI = unreceived[asset_name]
|
||||
var/keep_local_name = dont_mutate_filenames \
|
||||
|| ACI.legacy \
|
||||
|| ACI.keep_local_name \
|
||||
|| (ACI.namespace && !ACI.namespace_parent)
|
||||
if (!keep_local_name)
|
||||
new_asset_name = "asset.[ACI.hash][ACI.ext]"
|
||||
log_asset("Sending asset `[asset_name]` to client `[client]` as `[new_asset_name]`")
|
||||
client << browse_rsc(ACI.resource, new_asset_name)
|
||||
|
||||
client.sent_assets[new_asset_name] = ACI.hash
|
||||
|
||||
addtimer(CALLBACK(client, /client/proc/asset_cache_update_json), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/// Precache files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
/datum/asset_transport/proc/send_assets_slow(client/client, list/files, filerate = 3)
|
||||
var/startingfilerate = filerate
|
||||
for (var/file in files)
|
||||
if (!client)
|
||||
break
|
||||
if (send_assets(client, file))
|
||||
if (!(--filerate))
|
||||
filerate = startingfilerate
|
||||
client.browse_queue_flush()
|
||||
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
/// Check the config is valid to load this transport
|
||||
/// Returns TRUE or FALSE
|
||||
/datum/asset_transport/proc/validate_config(log = TRUE)
|
||||
return TRUE
|
||||
@@ -0,0 +1,87 @@
|
||||
/// CDN Webroot asset transport.
|
||||
/datum/asset_transport/webroot
|
||||
name = "CDN Webroot asset transport"
|
||||
|
||||
/datum/asset_transport/webroot/Load()
|
||||
if (validate_config(log = FALSE))
|
||||
load_existing_assets()
|
||||
|
||||
/// Processes thru any assets that were registered before we were loaded as a transport.
|
||||
/datum/asset_transport/webroot/proc/load_existing_assets()
|
||||
for (var/asset_name in SSassets.cache)
|
||||
var/datum/asset_cache_item/ACI = SSassets.cache[asset_name]
|
||||
save_asset_to_webroot(ACI)
|
||||
|
||||
/// Register a browser asset with the asset cache system
|
||||
/// We also save it to the CDN webroot at this step instead of waiting for send_assets()
|
||||
/// asset_name - the identifier of the asset
|
||||
/// asset - the actual asset file or an asset_cache_item datum.
|
||||
/datum/asset_transport/webroot/register_asset(asset_name, asset)
|
||||
. = ..()
|
||||
var/datum/asset_cache_item/ACI = .
|
||||
|
||||
if (istype(ACI) && ACI.hash)
|
||||
save_asset_to_webroot(ACI)
|
||||
|
||||
/// Saves the asset to the webroot taking into account namespaces and hashes.
|
||||
/datum/asset_transport/webroot/proc/save_asset_to_webroot(datum/asset_cache_item/ACI)
|
||||
var/webroot = CONFIG_GET(string/asset_cdn_webroot)
|
||||
var/newpath = "[webroot][get_asset_suffex(ACI)]"
|
||||
if (fexists(newpath))
|
||||
return
|
||||
if (fexists("[newpath].gz")) //its a common pattern in webhosting to save gzip'ed versions of text files and let the webserver serve them up as gzip compressed normal files, sometimes without keeping the original version.
|
||||
return
|
||||
return fcopy(ACI.resource, newpath)
|
||||
|
||||
/// Returns a url for a given asset.
|
||||
/// asset_name - Name of the asset.
|
||||
/// asset_cache_item - asset cache item datum for the asset, optional, overrides asset_name
|
||||
/datum/asset_transport/webroot/get_asset_url(asset_name, datum/asset_cache_item/asset_cache_item)
|
||||
if (!istype(asset_cache_item))
|
||||
asset_cache_item = SSassets.cache[asset_name]
|
||||
var/url = CONFIG_GET(string/asset_cdn_url) //config loading will handle making sure this ends in a /
|
||||
return "[url][get_asset_suffex(asset_cache_item)]"
|
||||
|
||||
/datum/asset_transport/webroot/proc/get_asset_suffex(datum/asset_cache_item/asset_cache_item)
|
||||
var/base = "[copytext(asset_cache_item.hash, 1, 3)]/"
|
||||
var/filename = "asset.[asset_cache_item.hash][asset_cache_item.ext]"
|
||||
if (length(asset_cache_item.namespace))
|
||||
base = "namespaces/[copytext(asset_cache_item.namespace, 1, 3)]/[asset_cache_item.namespace]/"
|
||||
if (!asset_cache_item.namespace_parent)
|
||||
filename = "[asset_cache_item.name]"
|
||||
return base + filename
|
||||
|
||||
|
||||
/// webroot asset sending - does nothing unless passed legacy assets
|
||||
/datum/asset_transport/webroot/send_assets(client/client, list/asset_list)
|
||||
. = FALSE
|
||||
var/list/legacy_assets = list()
|
||||
if (!islist(asset_list))
|
||||
asset_list = list(asset_list)
|
||||
for (var/asset_name in asset_list)
|
||||
var/datum/asset_cache_item/ACI = asset_list[asset_name]
|
||||
if (!istype(ACI))
|
||||
ACI = SSassets.cache[asset_name]
|
||||
if (!ACI)
|
||||
legacy_assets += asset_name //pass it on to base send_assets so it can output an error
|
||||
continue
|
||||
if (ACI.legacy)
|
||||
legacy_assets[asset_name] = ACI
|
||||
if (length(legacy_assets))
|
||||
. = ..(client, legacy_assets)
|
||||
|
||||
|
||||
/// webroot slow asset sending - does nothing.
|
||||
/datum/asset_transport/webroot/send_assets_slow(client/client, list/files, filerate)
|
||||
return FALSE
|
||||
|
||||
/datum/asset_transport/webroot/validate_config(log = TRUE)
|
||||
if (!CONFIG_GET(string/asset_cdn_url))
|
||||
if (log)
|
||||
log_asset("ERROR: [type]: Invalid Config: ASSET_CDN_URL")
|
||||
return FALSE
|
||||
if (!CONFIG_GET(string/asset_cdn_webroot))
|
||||
if (log)
|
||||
log_asset("ERROR: [type]: Invalid Config: ASSET_CDN_WEBROOT")
|
||||
return FALSE
|
||||
return TRUE
|
||||
@@ -26,4 +26,4 @@
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/turf
|
||||
//used for temperature calculations
|
||||
var/thermal_conductivity = 0.05
|
||||
var/thermal_conductivity = 0.005
|
||||
var/heat_capacity = 1
|
||||
var/temperature_archived
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
|
||||
/turf/proc/super_conduct()
|
||||
var/conductivity_directions = conductivity_directions()
|
||||
|
||||
archive()
|
||||
if(conductivity_directions)
|
||||
//Conduct with tiles around me
|
||||
for(var/direction in GLOB.cardinals)
|
||||
@@ -331,6 +331,7 @@
|
||||
var/heat = thermal_conductivity*delta_temperature* \
|
||||
(heat_capacity*HEAT_CAPACITY_VACUUM/(heat_capacity+HEAT_CAPACITY_VACUUM))
|
||||
temperature -= heat/heat_capacity
|
||||
temperature = max(temperature,T0C) //otherwise we just sorta get stuck at super cold temps forever
|
||||
|
||||
/turf/open/proc/temperature_share_open_to_solid(turf/sharer)
|
||||
sharer.temperature = air.temperature_share(null, sharer.thermal_conductivity, sharer.temperature, sharer.heat_capacity)
|
||||
@@ -344,3 +345,5 @@
|
||||
|
||||
temperature -= heat/heat_capacity
|
||||
sharer.temperature += heat/sharer.heat_capacity
|
||||
temperature = max(temperature,T0C)
|
||||
sharer.temperature = max(sharer.temperature,T0C)
|
||||
|
||||
@@ -188,7 +188,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
//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/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
|
||||
/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient,temperature=null,heat_capacity=null)
|
||||
//Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
|
||||
//Returns: new temperature of the sharer
|
||||
|
||||
@@ -196,7 +196,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
//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/proc/react(turf/open/dump_location)
|
||||
/datum/gas_mixture/proc/react(datum/holder)
|
||||
//Performs various reactions such as combustion or fusion (LOL)
|
||||
//Returns: 1 if any reaction took place; 0 otherwise
|
||||
|
||||
@@ -243,7 +243,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
set_moles(path, text2num(gas[id]))
|
||||
archive()
|
||||
return 1
|
||||
|
||||
/*
|
||||
/datum/gas_mixture/react(datum/holder)
|
||||
. = NO_REACTION
|
||||
if(!total_moles())
|
||||
@@ -291,7 +291,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
. |= reaction.react(src, holder)
|
||||
if (. & STOP_REACTIONS)
|
||||
break
|
||||
|
||||
*/
|
||||
//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:
|
||||
|
||||
@@ -186,6 +186,20 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/g
|
||||
moles_visible = MOLES_GAS_VISIBLE * 60
|
||||
rarity = 250
|
||||
|
||||
/datum/gas/methane
|
||||
id = "methane"
|
||||
specific_heat = 30
|
||||
name = "Methane"
|
||||
rarity = 320
|
||||
|
||||
/datum/gas/methyl_bromide
|
||||
id = "methyl_bromide"
|
||||
specific_heat = 42
|
||||
name = "Methyl Bromide"
|
||||
dangerous = TRUE
|
||||
rarity = 310
|
||||
|
||||
|
||||
/obj/effect/overlay/gas
|
||||
icon = 'icons/effects/atmospherics.dmi'
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
/datum/gas_reaction/proc/react(datum/gas_mixture/air, atom/location)
|
||||
return NO_REACTION
|
||||
|
||||
/datum/gas_reaction/proc/test()
|
||||
return list("success" = TRUE)
|
||||
|
||||
/datum/gas_reaction/nobliumsupression
|
||||
priority = INFINITY
|
||||
name = "Hyper-Noblium Reaction Suppression"
|
||||
@@ -70,6 +73,8 @@
|
||||
air.adjust_moles(/datum/gas/water_vapor,-MOLES_GAS_VISIBLE)
|
||||
. = REACTING
|
||||
|
||||
// no test cause it's entirely based on location
|
||||
|
||||
//tritium combustion: combustion of oxygen and tritium (treated as hydrocarbons). creates hotspots. exothermic
|
||||
/datum/gas_reaction/tritfire
|
||||
priority = -1 //fire should ALWAYS be last, but tritium fires happen before plasma fires
|
||||
@@ -126,6 +131,18 @@
|
||||
|
||||
return cached_results["fire"] ? REACTING : NO_REACTION
|
||||
|
||||
/datum/gas_reaction/tritfire/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/tritium,50)
|
||||
G.set_moles(/datum/gas/oxygen,50)
|
||||
G.set_temperature(500)
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(!G.reaction_results["fire"])
|
||||
return list("success" = FALSE, "message" = "Trit fires aren't setting fire results correctly!")
|
||||
return ..()
|
||||
|
||||
//plasma combustion: combustion of oxygen and plasma (treated as hydrocarbons). creates hotspots. exothermic
|
||||
/datum/gas_reaction/plasmafire
|
||||
priority = -2 //fire should ALWAYS be last, but plasma fires happen after tritium fires
|
||||
@@ -198,6 +215,28 @@
|
||||
|
||||
return cached_results["fire"] ? REACTING : NO_REACTION
|
||||
|
||||
/datum/gas_reaction/plasmafire/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/plasma,50)
|
||||
G.set_moles(/datum/gas/oxygen,50)
|
||||
G.set_volume(1000)
|
||||
G.set_temperature(500)
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(!G.reaction_results["fire"])
|
||||
return list("success" = FALSE, "message" = "Plasma fires aren't setting fire results correctly!")
|
||||
if(!G.get_moles(/datum/gas/carbon_dioxide))
|
||||
return list("success" = FALSE, "message" = "Plasma fires aren't making CO2!")
|
||||
G.clear()
|
||||
G.set_moles(/datum/gas/plasma,10)
|
||||
G.set_moles(/datum/gas/oxygen,1000)
|
||||
G.set_temperature(500)
|
||||
result = G.react()
|
||||
if(!G.get_moles(/datum/gas/tritium))
|
||||
return list("success" = FALSE, "message" = "Plasma fires aren't making trit!")
|
||||
return ..()
|
||||
|
||||
//fusion: a terrible idea that was fun but broken. Now reworked to be less broken and more interesting. Again (and again, and again). Again!
|
||||
//Fusion Rework Counter: Please increment this if you make a major overhaul to this system again.
|
||||
//6 reworks
|
||||
@@ -236,7 +275,7 @@
|
||||
for (var/gas_id in air.get_gases())
|
||||
gas_power += (gas_fusion_powers[gas_id]*air.get_moles(gas_id))
|
||||
var/instability = MODULUS((gas_power*INSTABILITY_GAS_POWER_FACTOR)**2,toroidal_size) //Instability effects how chaotic the behavior of the reaction is
|
||||
cached_scan_results[id] = instability//used for analyzer feedback
|
||||
cached_scan_results["fusion"] = instability//used for analyzer feedback
|
||||
|
||||
var/plasma = (initial_plasma-FUSION_MOLE_THRESHOLD)/(scale_factor) //We have to scale the amounts of carbon and plasma down a significant amount in order to show the chaotic dynamics we want
|
||||
var/carbon = (initial_carbon-FUSION_MOLE_THRESHOLD)/(scale_factor) //We also subtract out the threshold amount to make it harder for fusion to burn itself out.
|
||||
@@ -282,6 +321,31 @@
|
||||
air.set_temperature(clamp(((air.return_temperature()*old_heat_capacity + reaction_energy)/new_heat_capacity),TCMB,INFINITY))
|
||||
return REACTING
|
||||
|
||||
/datum/gas_reaction/fusion/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/carbon_dioxide,300)
|
||||
G.set_moles(/datum/gas/plasma,1000)
|
||||
G.set_moles(/datum/gas/tritium,100.61)
|
||||
G.set_moles(/datum/gas/nitryl,1)
|
||||
G.set_temperature(15000)
|
||||
G.set_volume(1000)
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(abs(G.analyzer_results["fusion"] - 3) > 0.0000001)
|
||||
var/instability = G.analyzer_results["fusion"]
|
||||
return list("success" = FALSE, "message" = "Fusion is not calculating analyzer results correctly, should be 3.000000045, is instead [instability]")
|
||||
if(abs(G.get_moles(/datum/gas/plasma) - 850.616) > 0.5)
|
||||
var/plas = G.get_moles(/datum/gas/plasma)
|
||||
return list("success" = FALSE, "message" = "Fusion is not calculating plasma correctly, should be 850.616, is instead [plas]")
|
||||
if(abs(G.get_moles(/datum/gas/carbon_dioxide) - 1699.384) > 0.5)
|
||||
var/co2 = G.get_moles(/datum/gas/carbon_dioxide)
|
||||
return list("success" = FALSE, "message" = "Fusion is not calculating co2 correctly, should be 1699.384, is instead [co2]")
|
||||
if(abs(G.return_temperature() - 27600) > 200) // calculating this manually sucks dude
|
||||
var/temp = G.return_temperature()
|
||||
return list("success" = FALSE, "message" = "Fusion is not calculating temperature correctly, should be around 27600, is instead [temp]")
|
||||
return ..()
|
||||
|
||||
/datum/gas_reaction/nitrylformation //The formation of nitryl. Endothermic. Requires N2O as a catalyst.
|
||||
priority = 3
|
||||
name = "Nitryl formation"
|
||||
@@ -313,6 +377,20 @@
|
||||
air.set_temperature(max(((temperature*old_heat_capacity - energy_used)/new_heat_capacity),TCMB))
|
||||
return REACTING
|
||||
|
||||
/datum/gas_reaction/nitrylformation/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/oxygen,30)
|
||||
G.set_moles(/datum/gas/nitrogen,30)
|
||||
G.set_moles(/datum/gas/nitrous_oxide,10)
|
||||
G.set_volume(1000)
|
||||
G.set_temperature(150000)
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(!G.get_moles(/datum/gas/nitryl) < 0.8)
|
||||
return list("success" = FALSE, "message" = "Nitryl isn't being generated correctly!")
|
||||
return ..()
|
||||
|
||||
/datum/gas_reaction/bzformation //Formation of BZ by combining plasma and tritium at low pressures. Exothermic.
|
||||
priority = 4
|
||||
name = "BZ Gas formation"
|
||||
@@ -348,6 +426,19 @@
|
||||
air.set_temperature(max(((temperature*old_heat_capacity + energy_released)/new_heat_capacity),TCMB))
|
||||
return REACTING
|
||||
|
||||
/datum/gas_reaction/bzformation/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/plasma,15)
|
||||
G.set_moles(/datum/gas/nitrous_oxide,15)
|
||||
G.set_volume(1000)
|
||||
G.set_temperature(10)
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(!G.get_moles(/datum/gas/bz) < 4) // efficiency is 4.0643 and bz generation == efficiency
|
||||
return list("success" = FALSE, "message" = "Nitryl isn't being generated correctly!")
|
||||
return ..()
|
||||
|
||||
/datum/gas_reaction/stimformation //Stimulum formation follows a strange pattern of how effective it will be at a given temperature, having some multiple peaks and some large dropoffs. Exo and endo thermic.
|
||||
priority = 5
|
||||
name = "Stimulum formation"
|
||||
@@ -380,6 +471,23 @@
|
||||
air.set_temperature(max(((air.return_temperature()*old_heat_capacity + stim_energy_change)/new_heat_capacity),TCMB))
|
||||
return REACTING
|
||||
|
||||
/datum/gas_reaction/stimformation/test()
|
||||
//above mentioned "strange pattern" is a basic quintic polynomial, it's fine, can calculate it manually
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/bz,30)
|
||||
G.set_moles(/datum/gas/plasma,1000)
|
||||
G.set_moles(/datum/gas/tritium,1000)
|
||||
G.set_moles(/datum/gas/nitryl,1000)
|
||||
G.set_volume(1000)
|
||||
G.set_temperature(12998000) // yeah, really
|
||||
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(!G.get_moles(/datum/gas/stimulum) < 900)
|
||||
return list("success" = FALSE, "message" = "Stimulum isn't being generated correctly!")
|
||||
return ..()
|
||||
|
||||
/datum/gas_reaction/nobliumformation //Hyper-Noblium formation is extrememly endothermic, but requires high temperatures to start. Due to its high mass, hyper-nobelium uses large amounts of nitrogen and tritium. BZ can be used as a catalyst to make it less endothermic.
|
||||
priority = 6
|
||||
name = "Hyper-Noblium condensation"
|
||||
@@ -408,6 +516,19 @@
|
||||
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
|
||||
air.set_temperature(max(((air.return_temperature()*old_heat_capacity - energy_taken)/new_heat_capacity),TCMB))
|
||||
|
||||
/datum/gas_reaction/nobliumformation/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/nitrogen,100)
|
||||
G.set_moles(/datum/gas/tritium,500)
|
||||
G.set_volume(1000)
|
||||
G.set_temperature(5000000) // yeah, really
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
if(abs(G.thermal_energy() - 23000000000) > 1000000) // god i hate floating points
|
||||
return list("success" = FALSE, "message" = "Hyper-nob formation isn't removing the right amount of heat! Should be 23,000,000,000, is instead [G.thermal_energy()]")
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/gas_reaction/miaster //dry heat sterilization: clears out pathogens in the air
|
||||
priority = -10 //after all the heating from fires etc. is done
|
||||
@@ -433,3 +554,20 @@
|
||||
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
|
||||
air.set_temperature(air.return_temperature() + cleaned_air * 0.002)
|
||||
SSresearch.science_tech.add_point_type(TECHWEB_POINT_TYPE_DEFAULT, cleaned_air*MIASMA_RESEARCH_AMOUNT)//Turns out the burning of miasma is kinda interesting to scientists
|
||||
|
||||
/datum/gas_reaction/miaster/test()
|
||||
var/datum/gas_mixture/G = new
|
||||
G.set_moles(/datum/gas/miasma,1)
|
||||
G.set_volume(1000)
|
||||
G.set_temperature(450)
|
||||
var/result = G.react()
|
||||
if(result != REACTING)
|
||||
return list("success" = FALSE, "message" = "Reaction didn't go at all!")
|
||||
G.clear()
|
||||
G.set_moles(/datum/gas/miasma,1)
|
||||
G.set_temperature(450)
|
||||
G.set_moles(/datum/gas/water_vapor,0.5)
|
||||
result = G.react()
|
||||
if(result != NO_REACTION)
|
||||
return list("success" = FALSE, "message" = "Miasma sterilization not stopping due to water vapor correctly!")
|
||||
return ..()
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
/datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa
|
||||
/datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000),
|
||||
/datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10),
|
||||
/datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5),
|
||||
/datum/gas/miasma = new/datum/tlv(-1, -1, 2, 5),
|
||||
/datum/gas/plasma = new/datum/tlv/dangerous,
|
||||
/datum/gas/nitrous_oxide = new/datum/tlv/dangerous,
|
||||
/datum/gas/bz = new/datum/tlv/dangerous,
|
||||
@@ -106,7 +106,9 @@
|
||||
/datum/gas/tritium = new/datum/tlv/dangerous,
|
||||
/datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects
|
||||
/datum/gas/nitryl = new/datum/tlv/dangerous,
|
||||
/datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
|
||||
/datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000), // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
|
||||
/datum/gas/methane = new/datum/tlv(-1, -1, 3, 6),
|
||||
/datum/gas/methyl_bromide = new/datum/tlv/dangerous
|
||||
)
|
||||
|
||||
/obj/machinery/airalarm/server // No checks here.
|
||||
@@ -125,7 +127,9 @@
|
||||
/datum/gas/tritium = new/datum/tlv/no_checks,
|
||||
/datum/gas/stimulum = new/datum/tlv/no_checks,
|
||||
/datum/gas/nitryl = new/datum/tlv/no_checks,
|
||||
/datum/gas/pluoxium = new/datum/tlv/no_checks
|
||||
/datum/gas/pluoxium = new/datum/tlv/no_checks,
|
||||
/datum/gas/methane = new/datum/tlv/no_checks,
|
||||
/datum/gas/methyl_bromide = new/datum/tlv/no_checks
|
||||
)
|
||||
|
||||
/obj/machinery/airalarm/kitchen_cold_room // Copypasta: to check temperatures.
|
||||
@@ -144,7 +148,9 @@
|
||||
/datum/gas/tritium = new/datum/tlv/dangerous,
|
||||
/datum/gas/stimulum = new/datum/tlv(-1, -1, 1000, 1000), // Stimulum has only positive effects
|
||||
/datum/gas/nitryl = new/datum/tlv/dangerous,
|
||||
/datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000) // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
|
||||
/datum/gas/pluoxium = new/datum/tlv(-1, -1, 1000, 1000), // Unlike oxygen, pluoxium does not fuel plasma/tritium fires
|
||||
/datum/gas/methane = new/datum/tlv(-1, -1, 3, 6),
|
||||
/datum/gas/methyl_bromide = new/datum/tlv/dangerous
|
||||
)
|
||||
|
||||
/obj/machinery/airalarm/unlocked
|
||||
@@ -542,7 +548,9 @@
|
||||
/datum/gas/tritium,
|
||||
/datum/gas/bz,
|
||||
/datum/gas/stimulum,
|
||||
/datum/gas/pluoxium
|
||||
/datum/gas/pluoxium,
|
||||
/datum/gas/methane,
|
||||
/datum/gas/methyl_bromide
|
||||
),
|
||||
"scrubbing" = 1,
|
||||
"widenet" = 1,
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
. = other_airs + air
|
||||
if(null in .)
|
||||
stack_trace("[src]([REF(src)]) has one or more null gas mixtures, which may cause bugs. Null mixtures will not be considered in reconcile_air().")
|
||||
return removeNullsFromList(.)
|
||||
return listclearnulls(.)
|
||||
|
||||
/datum/pipeline/proc/reconcile_air()
|
||||
var/list/datum/gas_mixture/GL = list()
|
||||
|
||||
@@ -50,7 +50,9 @@
|
||||
"stimulum" = /obj/machinery/portable_atmospherics/canister/stimulum,
|
||||
"pluoxium" = /obj/machinery/portable_atmospherics/canister/pluoxium,
|
||||
"caution" = /obj/machinery/portable_atmospherics/canister,
|
||||
"miasma" = /obj/machinery/portable_atmospherics/canister/miasma
|
||||
"miasma" = /obj/machinery/portable_atmospherics/canister/miasma,
|
||||
"methane" = /obj/machinery/portable_atmospherics/canister/methane,
|
||||
"methyl bromide" = /obj/machinery/portable_atmospherics/canister/methyl_bromide
|
||||
)
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/interact(mob/user)
|
||||
@@ -62,7 +64,7 @@
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/nitrogen
|
||||
name = "n2 canister"
|
||||
desc = "Nitrogen gas. Reportedly useful for something."
|
||||
desc = "Nitrogen. Reportedly useful for something."
|
||||
icon_state = "red"
|
||||
gas_type = /datum/gas/nitrogen
|
||||
|
||||
@@ -80,19 +82,19 @@
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/toxins
|
||||
name = "plasma canister"
|
||||
desc = "Plasma gas. The reason YOU are here. Highly toxic."
|
||||
desc = "Plasma. The reason YOU are here. Highly toxic."
|
||||
icon_state = "orange"
|
||||
gas_type = /datum/gas/plasma
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/bz
|
||||
name = "\improper BZ canister"
|
||||
desc = "BZ, a powerful hallucinogenic nerve agent."
|
||||
desc = "BZ. A powerful hallucinogenic nerve agent."
|
||||
icon_state = "purple"
|
||||
gas_type = /datum/gas/bz
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/nitrous_oxide
|
||||
name = "n2o canister"
|
||||
desc = "Nitrous oxide gas. Known to cause drowsiness."
|
||||
desc = "Nitrous oxide. Known to cause drowsiness."
|
||||
icon_state = "redws"
|
||||
gas_type = /datum/gas/nitrous_oxide
|
||||
|
||||
@@ -115,7 +117,7 @@
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/nitryl
|
||||
name = "nitryl canister"
|
||||
desc = "Nitryl gas. Feels great 'til the acid eats your lungs."
|
||||
desc = "Nitryl. Feels great 'til the acid eats your lungs."
|
||||
icon_state = "brown"
|
||||
gas_type = /datum/gas/nitryl
|
||||
|
||||
@@ -133,7 +135,7 @@
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/water_vapor
|
||||
name = "water vapor canister"
|
||||
desc = "Water Vapor. We get it, you vape."
|
||||
desc = "Water vapor. We get it, you vape."
|
||||
icon_state = "water_vapor"
|
||||
gas_type = /datum/gas/water_vapor
|
||||
filled = 1
|
||||
@@ -145,6 +147,18 @@
|
||||
gas_type = /datum/gas/miasma
|
||||
filled = 1
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/methane
|
||||
name = "methane canister"
|
||||
desc = "Methane. The simplest of hydrocarbons. Non-toxic but highly flammable."
|
||||
icon_state = "greyblackred"
|
||||
gas_type = /datum/gas/methane
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/methyl_bromide
|
||||
name = "methyl bromide canister"
|
||||
desc = "Methyl bromide. A potent toxin to most, essential for the Kharmaan to live."
|
||||
icon_state = "purplecyan"
|
||||
gas_type = /datum/gas/methyl_bromide
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/proc/get_time_left()
|
||||
if(timing)
|
||||
. = round(max(0, valve_timer - world.time) / 10, 1)
|
||||
|
||||
@@ -63,9 +63,9 @@
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(is_operational())
|
||||
if(prob(50 / severity))
|
||||
if(prob(severity/2))
|
||||
on = !on
|
||||
if(prob(100 / severity))
|
||||
if(prob(severity))
|
||||
direction = PUMP_OUT
|
||||
pump.target_pressure = rand(0, 100 * ONE_ATMOSPHERE)
|
||||
update_icon()
|
||||
@@ -80,7 +80,6 @@
|
||||
else if(on && holding && direction == PUMP_OUT)
|
||||
investigate_log("[key_name(user)] started a transfer into [holding].", INVESTIGATE_ATMOS)
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(is_operational())
|
||||
if(prob(50 / severity))
|
||||
if(prob(severity/3))
|
||||
on = !on
|
||||
update_icon()
|
||||
|
||||
|
||||
@@ -11,15 +11,15 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
/datum/gateway_destination
|
||||
var/name = "Unknown Destination"
|
||||
var/wait = 0 /// How long after roundstart this destination becomes active
|
||||
var/enabled = TRUE /// If disabled, the destination won't be availible
|
||||
var/enabled = TRUE /// If disabled, the destination won't be available
|
||||
var/hidden = FALSE /// Will not show on gateway controls at all.
|
||||
|
||||
/* Can a gateway link to this destination right now. */
|
||||
/datum/gateway_destination/proc/is_availible()
|
||||
/datum/gateway_destination/proc/is_available()
|
||||
return enabled && (world.time - SSticker.round_start_time >= wait)
|
||||
|
||||
/* Returns user-friendly description why you can't connect to this destination, displayed in UI */
|
||||
/datum/gateway_destination/proc/get_availible_reason()
|
||||
/datum/gateway_destination/proc/get_available_reason()
|
||||
. = "Unreachable"
|
||||
if(world.time - SSticker.round_start_time < wait)
|
||||
. = "Connection desynchronized. Recalibration in progress."
|
||||
@@ -52,8 +52,8 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
. = list()
|
||||
.["ref"] = REF(src)
|
||||
.["name"] = name
|
||||
.["availible"] = is_availible()
|
||||
.["reason"] = get_availible_reason()
|
||||
.["available"] = is_available()
|
||||
.["reason"] = get_available_reason()
|
||||
if(wait)
|
||||
.["timeout"] = max(1 - (wait - (world.time - SSticker.round_start_time)) / wait, 0)
|
||||
|
||||
@@ -72,10 +72,10 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
if(target_gateway.target == deactivated.destination)
|
||||
target_gateway.deactivate()
|
||||
|
||||
/datum/gateway_destination/gateway/is_availible()
|
||||
/datum/gateway_destination/gateway/is_available()
|
||||
return ..() && target_gateway.calibrated && !target_gateway.target && target_gateway.powered()
|
||||
|
||||
/datum/gateway_destination/gateway/get_availible_reason()
|
||||
/datum/gateway_destination/gateway/get_available_reason()
|
||||
. = ..()
|
||||
if(!target_gateway.calibrated)
|
||||
. = "Exit gateway malfunction. Manual recalibration required."
|
||||
@@ -217,8 +217,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
target = D
|
||||
target.activate(destination)
|
||||
generate_bumper()
|
||||
if(use_power == IDLE_POWER_USE)
|
||||
use_power = ACTIVE_POWER_USE
|
||||
use_power = ACTIVE_POWER_USE
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/gateway/proc/Transfer(atom/movable/AM)
|
||||
@@ -261,8 +260,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
if(!GLOB.the_gateway)
|
||||
to_chat(user,"<span class='warning'>Home gateway is not responding!</span>")
|
||||
if(GLOB.the_gateway.target)
|
||||
to_chat(user,"<span class='warning'>Home gateway already in use!</span>")
|
||||
return
|
||||
GLOB.the_gateway.deactivate() //this will turn the home gateway off so that it's free for us to connect to
|
||||
activate(GLOB.the_gateway.destination)
|
||||
else
|
||||
deactivate()
|
||||
@@ -319,7 +317,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
|
||||
/obj/machinery/computer/gateway_control/proc/try_to_connect(datum/gateway_destination/D)
|
||||
if(!D || !G)
|
||||
return
|
||||
if(!D.is_availible() || G.target)
|
||||
if(!D.is_available() || G.target)
|
||||
return
|
||||
G.activate(D)
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
if("Immortality")
|
||||
to_chat(user, "<B>Your wish is granted, but at a terrible cost...</B>")
|
||||
to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart.")
|
||||
user.verbs += /mob/living/carbon/proc/immortality
|
||||
add_verb(user, /mob/living/carbon/proc/immortality)
|
||||
user.set_species(/datum/species/shadow)
|
||||
if("To Kill")
|
||||
to_chat(user, "<B>Your wish is granted, but at a terrible cost...</B>")
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
cost = 300
|
||||
contains = list(/obj/item/storage/toolbox/mechanical)
|
||||
|
||||
/datum/supply_pack/goody/electrical_toolbox // mostly just to water down coupon probability
|
||||
name = "Mechanical Toolbox"
|
||||
/datum/supply_pack/goody/electrical_toolbox
|
||||
name = "Electrical Toolbox"
|
||||
desc = "A fully stocked electrical toolbox, for when you're too lazy to just print them out."
|
||||
cost = 300
|
||||
contains = list(/obj/item/storage/toolbox/electrical)
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
//////////////////// Paperwork and Writing Supplies //////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/datum/supply_pack/misc/anvil
|
||||
name = "Anvil Crate"
|
||||
desc = "An anvil in a crate, we had to dig this out of the old warehouse. It's got wheels on it so you can move it."
|
||||
cost = 7500
|
||||
contains = list(/obj/structure/anvil/obtainable/basic)
|
||||
|
||||
/datum/supply_pack/misc/artsupply
|
||||
name = "Art Supplies"
|
||||
desc = "Make some happy little accidents with six canvasses, two easels, two boxes of crayons, and a rainbow crayon!"
|
||||
|
||||
@@ -192,3 +192,49 @@
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
dangerous = TRUE
|
||||
|
||||
//////// RAW ANOMALY CORES
|
||||
|
||||
/datum/supply_pack/science/raw_flux_anomaly
|
||||
name = "Raw Flux Anomaly"
|
||||
desc = "The raw core of a flux anomaly, ready to be implosion-compressed into a powerful artifact."
|
||||
cost = 5000
|
||||
access = ACCESS_TOX
|
||||
contains = list(/obj/item/raw_anomaly_core/flux)
|
||||
crate_name = "raw flux anomaly"
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
|
||||
/datum/supply_pack/science/raw_grav_anomaly
|
||||
name = "Raw Gravitational Anomaly"
|
||||
desc = "The raw core of a gravitational anomaly, ready to be implosion-compressed into a powerful artifact."
|
||||
cost = 5000
|
||||
access = ACCESS_TOX
|
||||
contains = list(/obj/item/raw_anomaly_core/grav)
|
||||
crate_name = "raw pyro anomaly"
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
|
||||
/datum/supply_pack/science/raw_vortex_anomaly
|
||||
name = "Raw Vortex Anomaly"
|
||||
desc = "The raw core of a vortex anomaly, ready to be implosion-compressed into a powerful artifact."
|
||||
cost = 5000
|
||||
access = ACCESS_TOX
|
||||
contains = list(/obj/item/raw_anomaly_core/vortex)
|
||||
crate_name = "raw vortex anomaly"
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
|
||||
/datum/supply_pack/science/raw_bluespace_anomaly
|
||||
name = "Raw Bluespace Anomaly"
|
||||
desc = "The raw core of a bluespace anomaly, ready to be implosion-compressed into a powerful artifact."
|
||||
cost = 5000
|
||||
access = ACCESS_TOX
|
||||
contains = list(/obj/item/raw_anomaly_core/bluespace)
|
||||
crate_name = "raw bluespace anomaly"
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
|
||||
/datum/supply_pack/science/raw_pyro_anomaly
|
||||
name = "Raw Pyro Anomaly"
|
||||
desc = "The raw core of a pyro anomaly, ready to be implosion-compressed into a powerful artifact."
|
||||
cost = 5000
|
||||
access = ACCESS_TOX
|
||||
contains = list(/obj/item/raw_anomaly_core/pyro)
|
||||
crate_name = "raw pyro anomaly"
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
////////////////
|
||||
//ADMIN THINGS//
|
||||
////////////////
|
||||
/// hides the byond verb panel as we use our own custom version
|
||||
show_verb_panel = FALSE
|
||||
///Contains admin info. Null if client is not an admin.
|
||||
var/datum/admins/holder = null
|
||||
var/datum/click_intercept = null // Needs to implement InterceptClickOn(user,params,atom) proc
|
||||
var/AI_Interact = 0
|
||||
@@ -75,11 +78,12 @@
|
||||
|
||||
var/inprefs = FALSE
|
||||
var/list/topiclimiter
|
||||
|
||||
///Used for limiting the rate of clicks sends by the client to avoid abuse
|
||||
var/list/clicklimiter
|
||||
|
||||
var/datum/chatOutput/chatOutput
|
||||
|
||||
var/list/credits //lazy list of all credit object bound to this client
|
||||
///lazy list of all credit object bound to this client
|
||||
var/list/credits
|
||||
|
||||
var/datum/player_details/player_details //these persist between logins/logouts during the same round.
|
||||
|
||||
@@ -122,6 +126,23 @@
|
||||
|
||||
/// Messages currently seen by this client
|
||||
var/list/seen_messages
|
||||
|
||||
/// datum wrapper for client view
|
||||
var/datum/view_data/view_size
|
||||
|
||||
/// our current tab
|
||||
var/stat_tab
|
||||
|
||||
/// whether our browser is ready or not yet
|
||||
var/statbrowser_ready = FALSE
|
||||
|
||||
/// list of all tabs
|
||||
var/list/panel_tabs = list()
|
||||
|
||||
/// list of tabs containing spells and abilities
|
||||
var/list/spell_tabs = list()
|
||||
/// list of tabs containing verbs
|
||||
var/list/verb_tabs = list()
|
||||
///A lazy list of atoms we've examined in the last EXAMINE_MORE_TIME (default 1.5) seconds, so that we will call [atom/proc/examine_more()] instead of [atom/proc/examine()] on them when examining
|
||||
var/list/recent_examines
|
||||
///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns
|
||||
@@ -151,3 +172,4 @@
|
||||
|
||||
//world.time of when the crew manifest can be accessed
|
||||
var/crew_manifest_delay
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
When somebody clicks a link in game, this Topic is called first.
|
||||
It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever]
|
||||
(if specified in the link). ie locate(hsrc).Topic()
|
||||
|
||||
Such links can be spoofed.
|
||||
|
||||
Because of this certain things MUST be considered whenever adding a Topic() for something:
|
||||
- Can it be fed harmful values which could cause runtimes?
|
||||
- Is the Topic call an admin-only thing?
|
||||
@@ -38,7 +40,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
var/asset_cache_job
|
||||
if(href_list["asset_cache_confirm_arrival"])
|
||||
asset_cache_job = asset_cache_confirm_arrival(href_list["asset_cache_confirm_arrival"])
|
||||
if(!asset_cache_job)
|
||||
if (!asset_cache_job)
|
||||
return
|
||||
|
||||
// Rate limiting
|
||||
@@ -100,7 +102,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
return
|
||||
|
||||
// Tgui Topic middleware
|
||||
if(!tgui_Topic(href_list))
|
||||
if(tgui_Topic(href_list))
|
||||
return
|
||||
|
||||
// Admin PM
|
||||
@@ -108,10 +110,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
cmd_admin_pm(href_list["priv_msg"],null)
|
||||
return
|
||||
|
||||
// CITADEL Start - Mentor PM
|
||||
// Mentor PM (cit.)
|
||||
if (citadel_client_procs(href_list))
|
||||
return
|
||||
// CITADEL End
|
||||
|
||||
switch(href_list["_src_"])
|
||||
if("holder")
|
||||
@@ -119,7 +120,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
if("usr")
|
||||
hsrc = mob
|
||||
if("mentor") // CITADEL
|
||||
hsrc = mentor_datum // CITADEL END
|
||||
hsrc = mentor_datum
|
||||
if("prefs")
|
||||
if (inprefs)
|
||||
return
|
||||
@@ -129,8 +130,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
return
|
||||
if("vars")
|
||||
return view_var_Topic(href,href_list,hsrc)
|
||||
if("chat")
|
||||
return chatOutput.Topic(href, href_list)
|
||||
|
||||
switch(href_list["action"])
|
||||
if("openLink")
|
||||
@@ -147,7 +146,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href=\"https://secure.byond.com/membership\">Click Here to find out more</a>.")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/*
|
||||
* Call back proc that should be checked in all paths where a client can send messages
|
||||
*
|
||||
@@ -210,14 +208,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
///////////
|
||||
//CONNECT//
|
||||
///////////
|
||||
#if (PRELOAD_RSC == 0)
|
||||
GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
#endif
|
||||
|
||||
/client/New(TopicData)
|
||||
world.SetConfig("APP/admin", ckey, "role=admin") //CITADEL EDIT - Allows admins to reboot in OOM situations
|
||||
world.SetConfig("APP/admin", ckey, "role=admin")
|
||||
var/tdata = TopicData //save this for later use
|
||||
chatOutput = new /datum/chatOutput(src)
|
||||
TopicData = null //Prevent calls to client.Topic from connect
|
||||
|
||||
if(connection != "seeker" && connection != "web")//Invalid connection type.
|
||||
@@ -226,6 +220,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
GLOB.clients += src
|
||||
GLOB.directory[ckey] = src
|
||||
|
||||
// Instantiate tgui panel
|
||||
tgui_panel = new(src)
|
||||
|
||||
GLOB.ahelp_tickets.ClientLogin(src)
|
||||
var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins.
|
||||
//Admin Authorisation
|
||||
@@ -240,7 +237,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
debug_tools_allowed = TRUE
|
||||
//END CITADEL EDIT
|
||||
else if(GLOB.deadmins[ckey])
|
||||
verbs += /client/proc/readmin
|
||||
add_verb(src, /client/proc/readmin)
|
||||
connecting_admin = TRUE
|
||||
if(CONFIG_GET(flag/autoadmin))
|
||||
if(!GLOB.admin_datums[ckey])
|
||||
@@ -266,7 +263,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
new /datum/admins(localhost_rank, ckey, 1, 1)
|
||||
//preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum)
|
||||
prefs = GLOB.preferences_datums[ckey]
|
||||
|
||||
if(prefs)
|
||||
prefs.parent = src
|
||||
else
|
||||
@@ -276,10 +272,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
|
||||
prefs.last_ip = address //these are gonna be used for banning
|
||||
prefs.last_id = computer_id //these are gonna be used for banning
|
||||
fps = prefs.clientfps
|
||||
fps = prefs.clientfps //(prefs.clientfps < 0) ? RECOMMENDED_FPS : prefs.clientfps
|
||||
|
||||
if(fexists(roundend_report_file()))
|
||||
verbs += /client/proc/show_previous_roundend_report
|
||||
add_verb(src, /client/proc/show_previous_roundend_report)
|
||||
|
||||
var/full_version = "[byond_version].[byond_build ? byond_build : "xxx"]"
|
||||
log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]")
|
||||
@@ -301,22 +297,26 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
alert_mob_dupe_login = TRUE
|
||||
if(matches)
|
||||
if(C)
|
||||
message_admins("<font color='red'><B>Notice: </B><font color='blue'>[key_name_admin(src)] has the same [matches] as [key_name_admin(C)].</font>")
|
||||
log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)].")
|
||||
message_admins("<span class='danger'><B>Notice: </B></span><span class='notice'>[key_name_admin(src)] has the same [matches] as [key_name_admin(C)].</span>")
|
||||
log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)].")
|
||||
else
|
||||
message_admins("<font color='red'><B>Notice: </B><font color='blue'>[key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). </font>")
|
||||
log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).")
|
||||
message_admins("<span class='danger'><B>Notice: </B></span><span class='notice'>[key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). </span>")
|
||||
log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).")
|
||||
|
||||
if(GLOB.player_details[ckey])
|
||||
player_details = GLOB.player_details[ckey]
|
||||
player_details.byond_version = full_version
|
||||
else
|
||||
player_details = new
|
||||
player_details = new(ckey)
|
||||
player_details.byond_version = full_version
|
||||
GLOB.player_details[ckey] = player_details
|
||||
|
||||
|
||||
. = ..() //calls mob.Login()
|
||||
// if (length(GLOB.stickybanadminexemptions))
|
||||
// GLOB.stickybanadminexemptions -= ckey
|
||||
// if (!length(GLOB.stickybanadminexemptions))
|
||||
// restore_stickybans()
|
||||
|
||||
if (byond_version >= 512)
|
||||
if (!byond_build || byond_build < 1386)
|
||||
@@ -336,7 +336,14 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
chatOutput.start() // Starts the chat
|
||||
// if(SSinput.initialized) placed here on tg.
|
||||
// set_macros()
|
||||
// update_movement_keys()
|
||||
|
||||
// Initialize tgui panel
|
||||
tgui_panel.initialize()
|
||||
src << browse(file('html/statbrowser.html'), "window=statbrowser")
|
||||
|
||||
|
||||
if(alert_mob_dupe_login)
|
||||
spawn()
|
||||
@@ -347,12 +354,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
connection_timeofday = world.timeofday
|
||||
winset(src, null, "command=\".configure graphics-hwmode on\"")
|
||||
var/cev = CONFIG_GET(number/client_error_version)
|
||||
var/ceb = CONFIG_GET(number/client_error_build)
|
||||
var/cwv = CONFIG_GET(number/client_warn_version)
|
||||
if (byond_version < cev) //Out of date client.
|
||||
if (byond_version < cev || (byond_version == cev && byond_build < ceb)) //Out of date client.
|
||||
to_chat(src, "<span class='danger'><b>Your version of BYOND is too old:</b></span>")
|
||||
to_chat(src, CONFIG_GET(string/client_error_message))
|
||||
to_chat(src, "Your version: [byond_version]")
|
||||
to_chat(src, "Required version: [cev] or later")
|
||||
to_chat(src, "Your version: [byond_version].[byond_build]")
|
||||
to_chat(src, "Required version: [cev].[ceb] or later")
|
||||
to_chat(src, "Visit <a href=\"https://secure.byond.com/download\">BYOND's website</a> to get the latest version of BYOND.")
|
||||
if (connecting_admin)
|
||||
to_chat(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade")
|
||||
@@ -464,6 +472,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
if (menuitem)
|
||||
menuitem.Load_checked(src)
|
||||
|
||||
// view_size = new(src, getScreenSize(prefs.widescreenpref))
|
||||
// view_size.resetFormat()
|
||||
// view_size.setZoomMode()
|
||||
// fit_viewport()
|
||||
Master.UpdateTickRate()
|
||||
|
||||
/client/proc/ensure_keys_set()
|
||||
@@ -477,13 +489,17 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
|
||||
/client/Del()
|
||||
if(!gc_destroyed)
|
||||
Destroy()
|
||||
Destroy() //Clean up signals and timers.
|
||||
return ..()
|
||||
|
||||
/client/Destroy()
|
||||
GLOB.clients -= src
|
||||
GLOB.directory -= ckey
|
||||
log_access("Logout: [key_name(src)]")
|
||||
GLOB.ahelp_tickets.ClientLogout(src)
|
||||
// SSserver_maint.UpdateHubStatus()
|
||||
if(credits)
|
||||
QDEL_LIST(credits)
|
||||
log_access("Logout: [key_name(src)]")
|
||||
if(holder)
|
||||
adminGreet(1)
|
||||
holder.owner = null
|
||||
@@ -505,16 +521,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
)
|
||||
|
||||
send2irc("Server", "[cheesy_message] (No admins online)")
|
||||
|
||||
GLOB.ahelp_tickets.ClientLogout(src)
|
||||
GLOB.directory -= ckey
|
||||
GLOB.clients -= src
|
||||
QDEL_LIST_ASSOC_VAL(char_render_holders)
|
||||
if(movingmob != null)
|
||||
movingmob.client_mobs_in_contents -= mob
|
||||
UNSETEMPTY(movingmob.client_mobs_in_contents)
|
||||
// seen_messages = null
|
||||
Master.UpdateTickRate()
|
||||
. = ..()
|
||||
. = ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/client/proc/set_client_age_from_db(connectiontopic)
|
||||
@@ -616,6 +629,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
var/datum/DBQuery/query_log_connection = SSdbcore.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`server_ip`,`server_port`,`round_id`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')),'[world.port]','[GLOB.round_id]','[sql_ckey]',INET_ATON('[sql_ip]'),'[sql_computerid]')")
|
||||
query_log_connection.Execute()
|
||||
qdel(query_log_connection)
|
||||
|
||||
// SSserver_maint.UpdateHubStatus()
|
||||
|
||||
if(new_player)
|
||||
player_age = -1
|
||||
. = player_age
|
||||
@@ -843,9 +859,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
|
||||
/client/proc/add_verbs_from_config()
|
||||
if(CONFIG_GET(flag/see_own_notes))
|
||||
verbs += /client/proc/self_notes
|
||||
add_verb(src, /client/proc/self_notes)
|
||||
if(CONFIG_GET(flag/use_exp_tracking))
|
||||
verbs += /client/proc/self_playtime
|
||||
add_verb(src, /client/proc/self_playtime)
|
||||
|
||||
|
||||
#undef UPLOAD_LIMIT
|
||||
@@ -857,40 +873,31 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
return inactivity
|
||||
return FALSE
|
||||
|
||||
//send resources to the client. It's here in its own proc so we can move it around easiliy if need be
|
||||
/// Send resources to the client.
|
||||
/// Sends both game resources and browser assets.
|
||||
/client/proc/send_resources()
|
||||
#if (PRELOAD_RSC == 0)
|
||||
var/static/next_external_rsc = 0
|
||||
if(GLOB.external_rsc_urls && GLOB.external_rsc_urls.len)
|
||||
next_external_rsc = WRAP(next_external_rsc+1, 1, GLOB.external_rsc_urls.len+1)
|
||||
preload_rsc = GLOB.external_rsc_urls[next_external_rsc]
|
||||
var/list/external_rsc_urls = CONFIG_GET(keyed_list/external_rsc_urls)
|
||||
if(length(external_rsc_urls))
|
||||
next_external_rsc = WRAP(next_external_rsc+1, 1, external_rsc_urls.len+1)
|
||||
preload_rsc = external_rsc_urls[next_external_rsc]
|
||||
#endif
|
||||
//get the common files
|
||||
getFiles(
|
||||
'html/search.js',
|
||||
'html/panels.css',
|
||||
'html/browser/common.css',
|
||||
'html/browser/scannernew.css',
|
||||
'html/browser/playeroptions.css',
|
||||
)
|
||||
|
||||
spawn (10) //removing this spawn causes all clients to not get verbs.
|
||||
|
||||
//load info on what assets the client has
|
||||
src << browse('code/modules/asset_cache/validate_assets.html', "window=asset_cache_browser")
|
||||
|
||||
//Precache the client with all other assets slowly, so as to not block other browse() calls
|
||||
getFilesSlow(src, SSassets.preload, register_asset = FALSE)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/getFilesSlow, src, SSassets.preload, FALSE), 5 SECONDS)
|
||||
if (CONFIG_GET(flag/asset_simple_preload))
|
||||
addtimer(CALLBACK(SSassets.transport, /datum/asset_transport.proc/send_assets_slow, src, SSassets.transport.preload), 5 SECONDS)
|
||||
|
||||
#if (PRELOAD_RSC == 0)
|
||||
for (var/name in GLOB.vox_sounds)
|
||||
var/file = GLOB.vox_sounds[name]
|
||||
Export("##action=load_rsc", file)
|
||||
stoplag()
|
||||
for (var/name in GLOB.vox_sounds_male)
|
||||
var/file = GLOB.vox_sounds_male[name]
|
||||
Export("##action=load_rsc", file)
|
||||
stoplag()
|
||||
#endif
|
||||
|
||||
|
||||
@@ -996,3 +1003,21 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
|
||||
/client/proc/can_have_part(part_name)
|
||||
return prefs.pref_species.mutant_bodyparts[part_name] || (part_name in GLOB.unlocked_mutant_parts)
|
||||
|
||||
/// compiles a full list of verbs and sends it to the browser
|
||||
/client/proc/init_verbs()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
var/list/verblist = list()
|
||||
verb_tabs.Cut()
|
||||
for(var/thing in (verbs + mob?.verbs))
|
||||
var/procpath/verb_to_init = thing
|
||||
if(!verb_to_init)
|
||||
continue
|
||||
if(verb_to_init.hidden)
|
||||
continue
|
||||
if(!istext(verb_to_init.category))
|
||||
continue
|
||||
verb_tabs |= verb_to_init.category
|
||||
verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
|
||||
src << output("[url_encode(json_encode(verb_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
//Darkmode preference by Kmc2000//
|
||||
|
||||
/*
|
||||
This lets you switch chat themes by using winset and CSS loading, you must relog to see this change (or rebuild your browseroutput datum)
|
||||
Things to note:
|
||||
If you change ANYTHING in interface/skin.dmf you need to change it here:
|
||||
Format:
|
||||
winset(src, "window as appears in skin.dmf after elem", "var to change = desired value")
|
||||
How this works:
|
||||
I've added a function to browseroutput.js which registers a cookie for darkmode and swaps the chat accordingly. You can find the button to do this under the "cog" icon next to the ping button (top right of chat)
|
||||
This then swaps the window theme automatically
|
||||
Thanks to spacemaniac and mcdonald for help with the JS side of this.
|
||||
*/
|
||||
|
||||
/client/proc/force_white_theme() //There's no way round it. We're essentially changing the skin by hand. It's painful but it works, and is the way Lummox suggested.
|
||||
//Main windows
|
||||
winset(src, "infowindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "info", "background-color = [COLOR_WHITEMODE_BACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "browseroutput", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "outputwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "mainwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND]")
|
||||
winset(src, "split", "background-color = [COLOR_WHITEMODE_BACKGROUND]")
|
||||
//Buttons
|
||||
winset(src, "changelog", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "rules", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "wiki", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "forum", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "github", "background-color = [COLOR_WHITEMODE_INFO_BUTTONS_BG];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "report-issue", "background-color = [COLOR_WHITEMODE_ISSUE_BUTTON_BG];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
//Status and verb tabs
|
||||
winset(src, "output", "background-color = [COLOR_WHITEMODE_BACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "statwindow", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "stat", "background-color = [COLOR_WHITEMODE_BACKGROUND];tab-background-color = [COLOR_WHITEMODE_DARKBACKGROUND];\
|
||||
text-color = [COLOR_WHITEMODE_TEXT];tab-text-color = [COLOR_WHITEMODE_TEXT];\
|
||||
prefix-color = [COLOR_WHITEMODE_TEXT];suffix-color = [COLOR_WHITEMODE_TEXT]")
|
||||
//Etc.
|
||||
winset(src, "say", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "asset_cache_browser", "background-color = [COLOR_WHITEMODE_DARKBACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
winset(src, "tooltip", "background-color = [COLOR_WHITEMODE_BACKGROUND];text-color = [COLOR_WHITEMODE_TEXT]")
|
||||
|
||||
/client/proc/force_dark_theme() //Inversely, if theyre using white theme and want to swap to the superior dark theme, let's get WINSET() ing
|
||||
//Main windows
|
||||
winset(src, "infowindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "info", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "browseroutput", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "outputwindow", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "mainwindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND]")
|
||||
winset(src, "split", "background-color = [COLOR_DARKMODE_BACKGROUND]")
|
||||
//Buttons
|
||||
winset(src, "changelog", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "rules", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "wiki", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "forum", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "github", "background-color = [COLOR_DARKMODE_INFO_BUTTONS_BG];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "report-issue", "background-color = [COLOR_DARKMODE_ISSUE_BUTTON_BG];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
//Status and verb tabs
|
||||
winset(src, "output", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "statwindow", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "stat", "background-color = [COLOR_DARKMODE_DARKBACKGROUND];tab-background-color = [COLOR_DARKMODE_BACKGROUND];\
|
||||
text-color = [COLOR_DARKMODE_TEXT];tab-text-color = [COLOR_DARKMODE_TEXT];\
|
||||
prefix-color = [COLOR_DARKMODE_TEXT];suffix-color = [COLOR_DARKMODE_TEXT]")
|
||||
//Etc.
|
||||
winset(src, "say", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "asset_cache_browser", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
winset(src, "tooltip", "background-color = [COLOR_DARKMODE_BACKGROUND];text-color = [COLOR_DARKMODE_TEXT]")
|
||||
@@ -8,6 +8,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/client/parent
|
||||
//doohickeys for savefiles
|
||||
var/path
|
||||
var/vr_path
|
||||
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
|
||||
var/max_save_slots = 16
|
||||
|
||||
@@ -95,7 +96,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/facial_hair_color = "000000" //Facial hair color
|
||||
var/skin_tone = "caucasian1" //Skin color
|
||||
var/use_custom_skin_tone = FALSE
|
||||
var/eye_color = "000000" //Eye color
|
||||
var/left_eye_color = "000000" //Eye color
|
||||
var/right_eye_color = "000000"
|
||||
var/split_eye_colors = FALSE
|
||||
var/datum/species/pref_species = new /datum/species/human() //Mutant race
|
||||
var/list/features = list("mcolor" = "FFFFFF",
|
||||
"mcolor2" = "FFFFFF",
|
||||
@@ -163,6 +166,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
)
|
||||
var/custom_speech_verb = "default" //if your say_mod is to be something other than your races
|
||||
var/custom_tongue = "default" //if your tongue is to be something other than your races
|
||||
var/modified_limbs = list() //prosthetic/amputated limbs
|
||||
var/chosen_limb_id //body sprite selected to load for the users limbs, null means default, is sanitized when loaded
|
||||
|
||||
/// Security record note section
|
||||
var/security_records
|
||||
@@ -246,6 +251,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
/// Which of the 5 persistent scar slots we randomly roll to load for this round, if enabled. Actually rolled in [/datum/preferences/proc/load_character(slot)]
|
||||
var/scars_index = 1
|
||||
|
||||
var/hide_ckey = FALSE //pref for hiding if your ckey shows round-end or not
|
||||
|
||||
/datum/preferences/New(client/C)
|
||||
parent = C
|
||||
|
||||
@@ -370,6 +377,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "[medical_records]"
|
||||
else
|
||||
dat += "[TextPreview(medical_records)]...<BR>"
|
||||
dat += "<br><a href='?_src_=prefs;preference=hide_ckey;task=input'><b>Hide ckey: [hide_ckey ? "Enabled" : "Disabled"]</b></a><br>"
|
||||
dat += "</tr></table>"
|
||||
|
||||
//Character Appearance
|
||||
@@ -425,12 +433,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<b>Gender:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=gender;task=input'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
|
||||
if(gender != NEUTER && pref_species.sexes)
|
||||
dat += "<b>Body Model:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=body_model'>[features["body_model"] == MALE ? "Masculine" : "Feminine"]</a><BR>"
|
||||
dat += "<b>Limb Modification:</b><BR>"
|
||||
dat += "<a href='?_src_=prefs;preference=modify_limbs;task=input'>Modify Limbs</a><BR>"
|
||||
for(var/modification in modified_limbs)
|
||||
if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
|
||||
dat += "<b>[modification]: [modified_limbs[modification][2]]</b><BR>"
|
||||
else
|
||||
dat += "<b>[modification]: [modified_limbs[modification][1]]</b><BR>"
|
||||
dat += "<BR>"
|
||||
dat += "<b>Species:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=species;task=input'>[pref_species.name]</a><BR>"
|
||||
dat += "<b>Custom Species Name:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=custom_species;task=input'>[custom_species ? custom_species : "None"]</a><BR>"
|
||||
dat += "<b>Random Body:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=all;task=random'>Randomize!</A><BR>"
|
||||
dat += "<b>Always Random Body:</b><a href='?_src_=prefs;preference=all'>[be_random_body ? "Yes" : "No"]</A><BR>"
|
||||
dat += "<br><b>Cycle background:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=cycle_bg;task=input'>[bgstate]</a><BR>"
|
||||
|
||||
var/use_skintones = pref_species.use_skintones
|
||||
if(use_skintones)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
@@ -460,15 +475,22 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<b>Sprite Size:</b> <a href='?_src_=prefs;preference=body_size;task=input'>[features["body_size"]*100]%</a><br>"
|
||||
|
||||
if((EYECOLOR in pref_species.species_traits) && !(NOEYES in pref_species.species_traits))
|
||||
|
||||
if(!use_skintones && !mutant_colors)
|
||||
dat += APPEARANCE_CATEGORY_COLUMN
|
||||
|
||||
dat += "<h3>Eye Color</h3>"
|
||||
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[eye_color];'> </span> <a href='?_src_=prefs;preference=eyes;task=input'>Change</a><BR>"
|
||||
|
||||
dat += "</td>"
|
||||
if(left_eye_color != right_eye_color)
|
||||
split_eye_colors = TRUE
|
||||
dat += "<h3>Heterochromia</h3>"
|
||||
dat += "</b><a style='display:block;width:100px' href='?_src_=prefs;preference=toggle_split_eyes;task=input'>[split_eye_colors ? "Enabled" : "Disabled"]</a>"
|
||||
if(!split_eye_colors)
|
||||
dat += "<h3>Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'> </span> <a href='?_src_=prefs;preference=eyes;task=input'>Change</a>"
|
||||
dat += "</td>"
|
||||
else
|
||||
dat += "<h3>Left Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[left_eye_color];'> </span> <a href='?_src_=prefs;preference=eye_left;task=input'>Change</a>"
|
||||
dat += "<h3>Right Eye Color</h3>"
|
||||
dat += "<span style='border: 1px solid #161616; background-color: #[right_eye_color];'> </span> <a href='?_src_=prefs;preference=eye_right;task=input'>Change</a><BR>"
|
||||
dat += "</td>"
|
||||
else if(use_skintones || mutant_colors)
|
||||
dat += "</td>"
|
||||
|
||||
@@ -521,6 +543,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
|
||||
if(length(pref_species.allowed_limb_ids))
|
||||
if(!chosen_limb_id || !(chosen_limb_id in pref_species.allowed_limb_ids))
|
||||
chosen_limb_id = pref_species.limbs_id || pref_species.id
|
||||
dat += "<h3>Body sprite</h3>"
|
||||
dat += "<a style='display:block;width:100px' href='?_src_=prefs;preference=bodysprite;task=input'>[chosen_limb_id]</a>"
|
||||
|
||||
if(mutant_category)
|
||||
dat += "</td>"
|
||||
mutant_category = 0
|
||||
@@ -849,6 +877,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
|
||||
dat += "<h2>Fetish content prefs</h2>"
|
||||
dat += "<b>Arousal:</b><a href='?_src_=prefs;preference=arousable'>[arousable == TRUE ? "Enabled" : "Disabled"]</a><BR>"
|
||||
dat += "<b>Genital examine text</b>:<a href='?_src_=prefs;preference=genital_examine'>[(cit_toggles & GENITAL_EXAMINE) ? "Enabled" : "Disabled"]</a><BR>"
|
||||
dat += "<b>Vore examine text</b>:<a href='?_src_=prefs;preference=vore_examine'>[(cit_toggles & VORE_EXAMINE) ? "Enabled" : "Disabled"]</a><BR>"
|
||||
dat += "<b>Voracious MediHound sleepers:</b> <a href='?_src_=prefs;preference=hound_sleeper'>[(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]</a><br>"
|
||||
dat += "<b>Hear Vore Sounds:</b> <a href='?_src_=prefs;preference=toggleeatingnoise'>[(cit_toggles & EATING_NOISES) ? "Yes" : "No"]</a><br>"
|
||||
dat += "<b>Hear Vore Digestion Sounds:</b> <a href='?_src_=prefs;preference=toggledigestionnoise'>[(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]</a><br>"
|
||||
@@ -1223,6 +1253,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
for(var/V in all_quirks)
|
||||
var/datum/quirk/T = SSquirks.quirks[V]
|
||||
bal -= initial(T.value)
|
||||
for(var/modification in modified_limbs)
|
||||
if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
|
||||
return bal + 1 //max 1 point regardless of how many prosthetics
|
||||
return bal
|
||||
|
||||
/datum/preferences/proc/GetPositiveQuirkCount()
|
||||
@@ -1350,7 +1383,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
socks = random_socks()
|
||||
socks_color = random_short_color()
|
||||
if(BODY_ZONE_PRECISE_EYES)
|
||||
eye_color = random_eye_color()
|
||||
var/random_eye_color = random_eye_color()
|
||||
left_eye_color = random_eye_color
|
||||
right_eye_color = random_eye_color
|
||||
if("s_tone")
|
||||
skin_tone = random_skin_tone()
|
||||
use_custom_skin_tone = null
|
||||
@@ -1438,6 +1473,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(!isnull(msg))
|
||||
features["ooc_notes"] = msg
|
||||
|
||||
if("hide_ckey")
|
||||
hide_ckey = !hide_ckey
|
||||
if(user)
|
||||
user.mind?.hide_ckey = hide_ckey
|
||||
|
||||
if("hair")
|
||||
var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null
|
||||
if(new_hair)
|
||||
@@ -1475,6 +1515,29 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if("cycle_bg")
|
||||
bgstate = next_list_item(bgstate, bgstate_options)
|
||||
|
||||
if("modify_limbs")
|
||||
var/limb_type = input(user, "Choose the limb to modify:", "Character Preference") as null|anything in LOADOUT_ALLOWED_LIMB_TARGETS
|
||||
if(limb_type)
|
||||
var/modification_type = input(user, "Choose the modification to the limb:", "Character Preference") as null|anything in LOADOUT_LIMBS
|
||||
if(modification_type)
|
||||
if(modification_type == LOADOUT_LIMB_PROSTHETIC)
|
||||
var/prosthetic_type = input(user, "Choose the type of prosthetic", "Character Preference") as null|anything in (list("prosthetic") + GLOB.prosthetic_limb_types)
|
||||
if(prosthetic_type)
|
||||
var/number_of_prosthetics = 0
|
||||
for(var/modification in modified_limbs)
|
||||
if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
|
||||
number_of_prosthetics += 1
|
||||
if(number_of_prosthetics >= MAXIMUM_LOADOUT_PROSTHETICS && !(limb_type in modified_limbs && modified_limbs[limb_type][1] == LOADOUT_LIMB_PROSTHETIC))
|
||||
to_chat(user, "<span class='danger'>You can only have up to two prosthetic limbs!</span>")
|
||||
else
|
||||
//save the actual prosthetic data
|
||||
modified_limbs[limb_type] = list(modification_type, prosthetic_type)
|
||||
else
|
||||
if(modification_type == LOADOUT_LIMB_NORMAL)
|
||||
modified_limbs -= limb_type
|
||||
else
|
||||
modified_limbs[limb_type] = list(modification_type)
|
||||
|
||||
if("underwear")
|
||||
var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_list
|
||||
if(new_underwear)
|
||||
@@ -1506,9 +1569,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
socks_color = sanitize_hexcolor(n_socks_color, 6)
|
||||
|
||||
if("eyes")
|
||||
var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference","#"+eye_color) as color|null
|
||||
var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference","#"+left_eye_color) as color|null
|
||||
if(new_eyes)
|
||||
eye_color = sanitize_hexcolor(new_eyes, 6)
|
||||
left_eye_color = sanitize_hexcolor(new_eyes, 6)
|
||||
right_eye_color = sanitize_hexcolor(new_eyes, 6)
|
||||
|
||||
if("eye_left")
|
||||
var/new_eyes = input(user, "Choose your character's left eye colour:", "Character Preference","#"+left_eye_color) as color|null
|
||||
if(new_eyes)
|
||||
left_eye_color = sanitize_hexcolor(new_eyes, 6)
|
||||
|
||||
if("eye_right")
|
||||
var/new_eyes = input(user, "Choose your character's right eye colour:", "Character Preference","#"+right_eye_color) as color|null
|
||||
if(new_eyes)
|
||||
right_eye_color = sanitize_hexcolor(new_eyes, 6)
|
||||
|
||||
if("toggle_split_eyes")
|
||||
split_eye_colors = !split_eye_colors
|
||||
right_eye_color = left_eye_color
|
||||
|
||||
if("species")
|
||||
var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_race_names
|
||||
@@ -1552,7 +1630,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_mutantcolor)
|
||||
if(new_mutantcolor == "#000000")
|
||||
features["mcolor"] = pref_species.default_color
|
||||
else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin
|
||||
else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
|
||||
features["mcolor"] = sanitize_hexcolor(new_mutantcolor, 6)
|
||||
else
|
||||
to_chat(user, "<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -1563,7 +1641,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_mutantcolor)
|
||||
if(new_mutantcolor == "#000000")
|
||||
features["mcolor2"] = pref_species.default_color
|
||||
else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin
|
||||
else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
|
||||
features["mcolor2"] = sanitize_hexcolor(new_mutantcolor, 6)
|
||||
else
|
||||
to_chat(user, "<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -1574,7 +1652,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_mutantcolor)
|
||||
if(new_mutantcolor == "#000000")
|
||||
features["mcolor3"] = pref_species.default_color
|
||||
else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin
|
||||
else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3]) // mutantcolors must be bright, but only if they affect the skin
|
||||
features["mcolor3"] = sanitize_hexcolor(new_mutantcolor, 6)
|
||||
else
|
||||
to_chat(user, "<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -1890,7 +1968,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_cockcolor)
|
||||
if(new_cockcolor == "#000000")
|
||||
features["cock_color"] = pref_species.default_color
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
|
||||
features["cock_color"] = sanitize_hexcolor(new_cockcolor, 6)
|
||||
else
|
||||
to_chat(user,"<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -1930,7 +2008,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_ballscolor)
|
||||
if(new_ballscolor == "#000000")
|
||||
features["balls_color"] = pref_species.default_color
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
|
||||
features["balls_color"] = sanitize_hexcolor(new_ballscolor, 6)
|
||||
else
|
||||
to_chat(user,"<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -1957,7 +2035,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_breasts_color)
|
||||
if(new_breasts_color == "#000000")
|
||||
features["breasts_color"] = pref_species.default_color
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
|
||||
features["breasts_color"] = sanitize_hexcolor(new_breasts_color, 6)
|
||||
else
|
||||
to_chat(user,"<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -1979,7 +2057,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/temp_hsv = RGBtoHSV(new_vagcolor)
|
||||
if(new_vagcolor == "#000000")
|
||||
features["vag_color"] = pref_species.default_color
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3])
|
||||
else if(ReadHSV(temp_hsv)[3] >= ReadHSV(MINIMUM_MUTANT_COLOR)[3])
|
||||
features["vag_color"] = sanitize_hexcolor(new_vagcolor, 6)
|
||||
else
|
||||
to_chat(user,"<span class='danger'>Invalid color. Your color is not bright enough.</span>")
|
||||
@@ -2092,8 +2170,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
else
|
||||
features["body_model"] = chosengender
|
||||
gender = chosengender
|
||||
facial_hair_style = random_facial_hair_style(gender)
|
||||
hair_style = random_hair_style(gender)
|
||||
|
||||
if("body_size")
|
||||
var/min = CONFIG_GET(number/body_size_min)
|
||||
@@ -2119,6 +2195,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/selected_custom_speech_verb = input(user, "Choose your desired speech verb (none means your species speech verb)", "Character Preference") as null|anything in GLOB.speech_verbs
|
||||
if(selected_custom_speech_verb)
|
||||
custom_speech_verb = selected_custom_speech_verb
|
||||
|
||||
if("bodysprite")
|
||||
var/selected_body_sprite = input(user, "Choose your desired body sprite", "Character Preference") as null|anything in pref_species.allowed_limb_ids
|
||||
if(selected_body_sprite)
|
||||
chosen_limb_id = selected_body_sprite //this gets sanitized before loading
|
||||
else
|
||||
switch(href_list["preference"])
|
||||
//CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj
|
||||
@@ -2342,6 +2423,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
parent.mob.hud_used.update_parallax_pref(parent.mob)
|
||||
|
||||
// Citadel edit - Prefs don't work outside of this. :c
|
||||
|
||||
if("genital_examine")
|
||||
cit_toggles ^= GENITAL_EXAMINE
|
||||
|
||||
if("vore_examine")
|
||||
cit_toggles ^= VORE_EXAMINE
|
||||
|
||||
if("hound_sleeper")
|
||||
cit_toggles ^= MEDIHOUND_SLEEPER
|
||||
|
||||
@@ -2451,7 +2539,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
ShowChoices(user)
|
||||
return 1
|
||||
|
||||
/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE)
|
||||
/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE, initial_spawn = FALSE)
|
||||
if(be_random_name)
|
||||
real_name = pref_species.random_name(gender)
|
||||
|
||||
@@ -2475,12 +2563,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
character.gender = gender
|
||||
character.age = age
|
||||
|
||||
character.eye_color = eye_color
|
||||
character.left_eye_color = left_eye_color
|
||||
character.right_eye_color = right_eye_color
|
||||
var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes)
|
||||
if(organ_eyes)
|
||||
if(!initial(organ_eyes.eye_color))
|
||||
organ_eyes.eye_color = eye_color
|
||||
organ_eyes.old_eye_color = eye_color
|
||||
if(!initial(organ_eyes.left_eye_color))
|
||||
organ_eyes.left_eye_color = left_eye_color
|
||||
organ_eyes.right_eye_color = right_eye_color
|
||||
organ_eyes.old_left_eye_color = left_eye_color
|
||||
organ_eyes.old_right_eye_color = right_eye_color
|
||||
character.hair_color = hair_color
|
||||
character.facial_hair_color = facial_hair_color
|
||||
character.skin_tone = skin_tone
|
||||
@@ -2510,6 +2601,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
|
||||
character.dna.features = features.Copy()
|
||||
character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE)
|
||||
if(chosen_limb_id && (chosen_limb_id in character.dna.species.allowed_limb_ids))
|
||||
character.dna.species.mutant_bodyparts["limbs_id"] = chosen_limb_id
|
||||
character.dna.real_name = character.real_name
|
||||
character.dna.nameless = character.nameless
|
||||
character.dna.custom_species = character.custom_species
|
||||
@@ -2544,6 +2637,35 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(custom_speech_verb != "default")
|
||||
character.dna.species.say_mod = custom_speech_verb
|
||||
|
||||
//limb stuff, only done when initially spawning in
|
||||
if(initial_spawn)
|
||||
//delete any existing prosthetic limbs to make sure no remnant prosthetics are left over - But DO NOT delete those that are species-related
|
||||
for(var/obj/item/bodypart/part in character.bodyparts)
|
||||
if(part.status == BODYPART_ROBOTIC && !part.render_like_organic)
|
||||
qdel(part)
|
||||
character.regenerate_limbs() //regenerate limbs so now you only have normal limbs
|
||||
for(var/modified_limb in modified_limbs)
|
||||
var/modification = modified_limbs[modified_limb][1]
|
||||
var/obj/item/bodypart/old_part = character.get_bodypart(modified_limb)
|
||||
if(modification == LOADOUT_LIMB_PROSTHETIC)
|
||||
var/obj/item/bodypart/new_limb
|
||||
switch(modified_limb)
|
||||
if(BODY_ZONE_L_ARM)
|
||||
new_limb = new/obj/item/bodypart/l_arm/robot/surplus(character)
|
||||
if(BODY_ZONE_R_ARM)
|
||||
new_limb = new/obj/item/bodypart/r_arm/robot/surplus(character)
|
||||
if(BODY_ZONE_L_LEG)
|
||||
new_limb = new/obj/item/bodypart/l_leg/robot/surplus(character)
|
||||
if(BODY_ZONE_R_LEG)
|
||||
new_limb = new/obj/item/bodypart/r_leg/robot/surplus(character)
|
||||
var/prosthetic_type = modified_limbs[modified_limb][2]
|
||||
if(prosthetic_type != "prosthetic") //lets just leave the old sprites as they are
|
||||
new_limb.icon = file("icons/mob/augmentation/cosmetic_prosthetic/[prosthetic_type].dmi")
|
||||
new_limb.replace_limb(character)
|
||||
qdel(old_part)
|
||||
|
||||
character.regenerate_icons()
|
||||
|
||||
SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
|
||||
|
||||
//let's be sure the character updates
|
||||
@@ -2551,6 +2673,18 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
character.update_body()
|
||||
character.update_hair()
|
||||
|
||||
/datum/preferences/proc/post_copy_to(mob/living/carbon/human/character)
|
||||
//if no legs, and not a paraplegic or a slime, give them a free wheelchair
|
||||
if(modified_limbs[BODY_ZONE_L_LEG] == LOADOUT_LIMB_AMPUTATED && modified_limbs[BODY_ZONE_R_LEG] == LOADOUT_LIMB_AMPUTATED && !character.has_quirk(/datum/quirk/paraplegic) && !isjellyperson(character))
|
||||
if(character.buckled)
|
||||
character.buckled.unbuckle_mob(character)
|
||||
var/turf/T = get_turf(character)
|
||||
var/obj/structure/chair/spawn_chair = locate() in T
|
||||
var/obj/vehicle/ridden/wheelchair/wheels = new(T)
|
||||
if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
|
||||
wheels.setDir(spawn_chair.dir)
|
||||
wheels.buckle_mob(character)
|
||||
|
||||
/datum/preferences/proc/get_default_name(name_id)
|
||||
switch(name_id)
|
||||
if("human")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// You do not need to raise this if you are adding new values that have sane defaults.
|
||||
// Only raise this value when changing the meaning/format/name/layout of an existing value
|
||||
// where you would want the updater procs below to run
|
||||
#define SAVEFILE_VERSION_MAX 35
|
||||
#define SAVEFILE_VERSION_MAX 36
|
||||
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
@@ -204,30 +204,39 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if(S["species"] == "lizard")
|
||||
features["mam_snouts"] = features["snout"]
|
||||
|
||||
if(current_version < 36)
|
||||
left_eye_color = S["eye_color"]
|
||||
right_eye_color = S["eye_color"]
|
||||
|
||||
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
|
||||
if(!ckey)
|
||||
return
|
||||
path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
|
||||
vr_path = "data/player_saves/[ckey[1]]/[ckey]/vore"
|
||||
|
||||
/datum/preferences/proc/load_preferences()
|
||||
if(!path)
|
||||
return 0
|
||||
return FALSE
|
||||
if(world.time < loadprefcooldown)
|
||||
if(istype(parent))
|
||||
to_chat(parent, "<span class='warning'>You're attempting to load your preferences a little too fast. Wait half a second, then try again.</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
loadprefcooldown = world.time + PREF_SAVELOAD_COOLDOWN
|
||||
if(!fexists(path))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return 0
|
||||
return FALSE
|
||||
S.cd = "/"
|
||||
|
||||
var/needs_update = savefile_needs_update(S)
|
||||
if(needs_update == -2) //fatal, can't load any data
|
||||
return 0
|
||||
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
|
||||
if (fexists(bacpath))
|
||||
fdel(bacpath) //only keep 1 version of backup
|
||||
fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
||||
return FALSE
|
||||
|
||||
. = TRUE
|
||||
|
||||
@@ -289,8 +298,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
if(needs_update >= 0)
|
||||
var/bacpath = "[path].updatebac" //todo: if the savefile version is higher then the server, check the backup, and give the player a prompt to load the backup
|
||||
if (fexists(bacpath))
|
||||
fdel(bacpath) //only keep 1 version of backup
|
||||
fcopy(S, bacpath) //byond helpfully lets you use a savefile for the first arg.
|
||||
update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer)
|
||||
|
||||
|
||||
|
||||
//Sanitize
|
||||
ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor)))
|
||||
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
|
||||
@@ -332,7 +347,25 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
|
||||
|
||||
return 1
|
||||
if(needs_update >= 0) //save the updated version
|
||||
var/old_default_slot = default_slot
|
||||
var/old_max_save_slots = max_save_slots
|
||||
|
||||
for (var/slot in S.dir) //but first, update all current character slots.
|
||||
if (copytext(slot, 1, 10) != "character")
|
||||
continue
|
||||
var/slotnum = text2num(copytext(slot, 10))
|
||||
if (!slotnum)
|
||||
continue
|
||||
max_save_slots = max(max_save_slots, slotnum) //so we can still update byond member slots after they lose memeber status
|
||||
default_slot = slotnum
|
||||
if (load_character()) // this updtates char slots
|
||||
save_character()
|
||||
default_slot = old_default_slot
|
||||
max_save_slots = old_max_save_slots
|
||||
save_preferences()
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/preferences/proc/verify_keybindings_valid()
|
||||
// Sanitize the actual keybinds to make sure they exist.
|
||||
@@ -422,17 +455,17 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
/datum/preferences/proc/load_character(slot)
|
||||
if(!path)
|
||||
return 0
|
||||
return FALSE
|
||||
if(world.time < loadcharcooldown) //This is before the check to see if the filepath exists to ensure that BYOND can't get hung up on read attempts when the hard drive is a little slow
|
||||
if(istype(parent))
|
||||
to_chat(parent, "<span class='warning'>You're attempting to load your character a little too fast. Wait half a second, then try again.</span>")
|
||||
return "SLOW THE FUCK DOWN" //the reason this isn't null is to make sure that people don't have their character slots overridden by random chars if they accidentally double-click a slot
|
||||
loadcharcooldown = world.time + PREF_SAVELOAD_COOLDOWN
|
||||
if(!fexists(path))
|
||||
return 0
|
||||
return FALSE
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return 0
|
||||
return FALSE
|
||||
S.cd = "/"
|
||||
if(!slot)
|
||||
slot = default_slot
|
||||
@@ -444,7 +477,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S.cd = "/character[slot]"
|
||||
var/needs_update = savefile_needs_update(S)
|
||||
if(needs_update == -2) //fatal, can't load any data
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
. = TRUE
|
||||
|
||||
@@ -462,7 +495,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
pref_species = new newtype
|
||||
|
||||
|
||||
scars_index = rand(1,5)
|
||||
scars_index = rand(1,5) // WHY
|
||||
|
||||
//Character
|
||||
S["real_name"] >> real_name
|
||||
@@ -476,7 +509,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["age"] >> age
|
||||
S["hair_color"] >> hair_color
|
||||
S["facial_hair_color"] >> facial_hair_color
|
||||
S["eye_color"] >> eye_color
|
||||
S["left_eye_color"] >> left_eye_color
|
||||
S["right_eye_color"] >> right_eye_color
|
||||
S["use_custom_skin_tone"] >> use_custom_skin_tone
|
||||
S["skin_tone"] >> skin_tone
|
||||
S["hair_style_name"] >> hair_style
|
||||
@@ -514,7 +548,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["scars3"] >> scars_list["3"]
|
||||
S["scars4"] >> scars_list["4"]
|
||||
S["scars5"] >> scars_list["5"]
|
||||
|
||||
var/limbmodstr
|
||||
S["modified_limbs"] >> limbmodstr
|
||||
if(length(limbmodstr))
|
||||
modified_limbs = safe_json_decode(limbmodstr)
|
||||
else
|
||||
modified_limbs = list()
|
||||
S["chosen_limb_id"] >> chosen_limb_id
|
||||
S["hide_ckey"] >> hide_ckey //saved per-character
|
||||
|
||||
//Custom names
|
||||
for(var/custom_name_id in GLOB.preferences_custom_names)
|
||||
@@ -597,8 +638,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
S["vore_flags"] >> vore_flags
|
||||
S["vore_taste"] >> vore_taste
|
||||
S["belly_prefs"] >> belly_prefs
|
||||
|
||||
var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
|
||||
if(fexists(char_vr_path))
|
||||
var/list/json_from_file = json_decode(file2text(char_vr_path))
|
||||
if(json_from_file)
|
||||
belly_prefs = json_from_file["belly_prefs"]
|
||||
//gear loadout
|
||||
var/text_to_load
|
||||
S["loadout"] >> text_to_load
|
||||
@@ -615,6 +659,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
gear_points -= init_cost
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
//preference updating will handle saving the updated data for us.
|
||||
if(needs_update >= 0)
|
||||
update_character(needs_update, S) //needs_update == savefile_version if we need an update (positive integer)
|
||||
|
||||
@@ -647,7 +692,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
|
||||
hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
|
||||
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
|
||||
eye_color = sanitize_hexcolor(eye_color, 6, FALSE)
|
||||
left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE)
|
||||
right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE)
|
||||
|
||||
var/static/allow_custom_skintones
|
||||
if(isnull(allow_custom_skintones))
|
||||
@@ -776,7 +822,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["age"] , age)
|
||||
WRITE_FILE(S["hair_color"] , hair_color)
|
||||
WRITE_FILE(S["facial_hair_color"] , facial_hair_color)
|
||||
WRITE_FILE(S["eye_color"] , eye_color)
|
||||
WRITE_FILE(S["left_eye_color"] , left_eye_color)
|
||||
WRITE_FILE(S["right_eye_color"] , right_eye_color)
|
||||
WRITE_FILE(S["use_custom_skin_tone"] , use_custom_skin_tone)
|
||||
WRITE_FILE(S["skin_tone"] , skin_tone)
|
||||
WRITE_FILE(S["hair_style_name"] , hair_style)
|
||||
@@ -856,13 +903,18 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["joblessrole"] , joblessrole)
|
||||
//Write prefs
|
||||
WRITE_FILE(S["job_preferences"] , job_preferences)
|
||||
WRITE_FILE(S["hide_ckey"] , hide_ckey)
|
||||
|
||||
//Quirks
|
||||
WRITE_FILE(S["all_quirks"] , all_quirks)
|
||||
|
||||
WRITE_FILE(S["vore_flags"] , vore_flags)
|
||||
WRITE_FILE(S["vore_taste"] , vore_taste)
|
||||
WRITE_FILE(S["belly_prefs"] , belly_prefs)
|
||||
var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
|
||||
var/belly_prefs_json = safe_json_encode(list("belly_prefs" = belly_prefs))
|
||||
if(fexists(char_vr_path))
|
||||
fdel(char_vr_path)
|
||||
text2file(belly_prefs_json,char_vr_path)
|
||||
|
||||
WRITE_FILE(S["persistent_scars"] , persistent_scars)
|
||||
WRITE_FILE(S["scars1"] , scars_list["1"])
|
||||
@@ -870,6 +922,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["scars3"] , scars_list["3"])
|
||||
WRITE_FILE(S["scars4"] , scars_list["4"])
|
||||
WRITE_FILE(S["scars5"] , scars_list["5"])
|
||||
if(islist(modified_limbs))
|
||||
WRITE_FILE(S["modified_limbs"] , safe_json_encode(modified_limbs))
|
||||
WRITE_FILE(S["chosen_limb_id"], chosen_limb_id)
|
||||
|
||||
|
||||
//gear loadout
|
||||
if(chosen_gear.len)
|
||||
|
||||
@@ -145,8 +145,7 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglemidis)()
|
||||
to_chat(usr, "You will no longer hear sounds uploaded by admins")
|
||||
usr.stop_sound_channel(CHANNEL_ADMIN)
|
||||
var/client/C = usr.client
|
||||
if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.stopMusic()
|
||||
C?.tgui_panel?.stop_music()
|
||||
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Hearing Midis", "[usr.client.prefs.toggles & SOUND_MIDI ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
/datum/verbs/menu/Settings/Sound/togglemidis/Get_checked(client/C)
|
||||
return C.prefs.toggles & SOUND_MIDI
|
||||
@@ -234,8 +233,7 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleprayersounds)()
|
||||
set desc = "Stop Current Sounds"
|
||||
SEND_SOUND(usr, sound(null))
|
||||
var/client/C = usr.client
|
||||
if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.stopMusic()
|
||||
C?.tgui_panel?.stop_music()
|
||||
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Stop Self Sounds")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
|
||||
@@ -158,88 +158,6 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
|
||||
else
|
||||
to_chat(src, "<span class='notice'>There are no admin notices at the moment.</span>")
|
||||
|
||||
/client/verb/fix_chat()
|
||||
set name = "Fix chat"
|
||||
set category = "OOC"
|
||||
if (!chatOutput || !istype(chatOutput))
|
||||
var/action = alert(src, "Invalid Chat Output data found!\nRecreate data?", "Wot?", "Recreate Chat Output data", "Cancel")
|
||||
if (action != "Recreate Chat Output data")
|
||||
return
|
||||
chatOutput = new /datum/chatOutput(src)
|
||||
chatOutput.start()
|
||||
action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
|
||||
if (action == "Fixed")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum")
|
||||
else
|
||||
chatOutput.load()
|
||||
action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
|
||||
if (action == "Yes")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum and forcing a load()")
|
||||
else
|
||||
action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
|
||||
if (action == "Switch to old chat")
|
||||
winset(src, "output", "is-visible=true;is-disabled=false")
|
||||
winset(src, "browseroutput", "is-visible=false")
|
||||
log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after recreating the chatOutput and forcing a load()")
|
||||
|
||||
else if (chatOutput.loaded)
|
||||
var/action = alert(src, "ChatOutput seems to be loaded\nDo you want me to force a reload, wiping the chat log or just refresh the chat window because it broke/went away?", "Hmmm", "Force Reload", "Refresh", "Cancel")
|
||||
switch (action)
|
||||
if ("Force Reload")
|
||||
chatOutput.loaded = FALSE
|
||||
chatOutput.start() //this is likely to fail since it asks , but we should try it anyways so we know.
|
||||
action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
|
||||
if (action == "Fixed")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a start()")
|
||||
else
|
||||
chatOutput.load()
|
||||
action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
|
||||
if (action == "Yes")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()")
|
||||
else
|
||||
action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
|
||||
if (action == "Switch to old chat")
|
||||
winset(src, "output", "is-visible=true;is-disabled=false")
|
||||
winset(src, "browseroutput", "is-visible=false")
|
||||
log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a start() and forcing a load()")
|
||||
|
||||
if ("Refresh")
|
||||
chatOutput.showChat()
|
||||
action = alert(src, "Goon chat refreshing, wait a bit and tell me if it's fixed", "", "Fixed", "Nope, force a reload")
|
||||
if (action == "Fixed")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a show()")
|
||||
else
|
||||
chatOutput.loaded = FALSE
|
||||
chatOutput.load()
|
||||
action = alert(src, "How about now? (give it a moment)", "", "Yes", "No")
|
||||
if (action == "Yes")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()")
|
||||
else
|
||||
action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
|
||||
if (action == "Switch to old chat")
|
||||
winset(src, "output", "is-visible=true;is-disabled=false")
|
||||
winset(src, "browseroutput", "is-visible=false")
|
||||
log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a show() and forcing a load()")
|
||||
return
|
||||
|
||||
else
|
||||
chatOutput.start()
|
||||
var/action = alert(src, "Manually loading Chat, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
|
||||
if (action == "Fixed")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start()")
|
||||
else
|
||||
chatOutput.load()
|
||||
alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
|
||||
if (action == "Yes")
|
||||
log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start() and forcing a load()")
|
||||
else
|
||||
action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
|
||||
if (action == "Switch to old chat")
|
||||
winset(src, "output", list2params(list("on-show" = "", "is-disabled" = "false", "is-visible" = "true")))
|
||||
winset(src, "browseroutput", "is-disabled=true;is-visible=false")
|
||||
log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after manually calling start() and forcing a load()")
|
||||
|
||||
|
||||
|
||||
/client/verb/motd()
|
||||
set name = "MOTD"
|
||||
@@ -362,3 +280,9 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
|
||||
|
||||
pct += delta
|
||||
winset(src, "mainwindow.split", "splitter=[pct]")
|
||||
|
||||
/client/verb/fix_stat_panel()
|
||||
set name = "Fix Stat Panel"
|
||||
set hidden = TRUE
|
||||
|
||||
init_verbs()
|
||||
|
||||
@@ -373,7 +373,7 @@
|
||||
|
||||
/obj/item/clothing/glasses/sunglasses/blindfold/white/update_icon(mob/living/carbon/human/user)
|
||||
if(ishuman(user) && !colored_before)
|
||||
add_atom_colour("#[user.eye_color]", FIXED_COLOUR_PRIORITY)
|
||||
add_atom_colour("#[user.left_eye_color]", FIXED_COLOUR_PRIORITY)
|
||||
colored_before = TRUE
|
||||
|
||||
/obj/item/clothing/glasses/sunglasses/blindfold/white/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
|
||||
@@ -382,7 +382,7 @@
|
||||
var/mob/living/carbon/human/H = loc
|
||||
var/mutable_appearance/M = mutable_appearance('icons/mob/clothing/eyes.dmi', "blindfoldwhite")
|
||||
M.appearance_flags |= RESET_COLOR
|
||||
M.color = "#[H.eye_color]"
|
||||
M.color = "#[H.left_eye_color]"
|
||||
. += M
|
||||
|
||||
/obj/item/clothing/glasses/sunglasses/big
|
||||
|
||||
@@ -72,6 +72,25 @@
|
||||
siemens_coefficient = 0
|
||||
permeability_coefficient = 0.05
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator
|
||||
name = "insidious guerrilla gloves"
|
||||
desc = "Specialized combat gloves for carrying people around. Transfers tactical kidnapping and tackling knowledge to the user via the use of nanochips."
|
||||
icon_state = "infiltrator"
|
||||
item_state = "infiltrator"
|
||||
siemens_coefficient = 0
|
||||
permeability_coefficient = 0.05
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
var/carrytrait = TRAIT_QUICKER_CARRY
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot == SLOT_GLOVES)
|
||||
ADD_TRAIT(user, carrytrait, GLOVE_TRAIT)
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat/insulated/infiltrator/dropped(mob/user)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(user, carrytrait, GLOVE_TRAIT)
|
||||
|
||||
/obj/item/clothing/gloves/tackler/rocket
|
||||
name = "rocket gloves"
|
||||
desc = "The ultimate in high risk, high reward, perfect for when you need to stop a criminal from fifty feet away or die trying. Banned in most Spinward gridiron football and rugby leagues."
|
||||
|
||||
@@ -62,13 +62,12 @@
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
var/mob/living/carbon/human/user = src.loc
|
||||
switch(severity)
|
||||
if(1)
|
||||
if(activated && user && ishuman(user) && (user.wear_suit == src))
|
||||
to_chat(user, "<span class='danger'>E:FATAL:RAM_READ_FAIL\nE:FATAL:STACK_EMPTY\nE:FATAL:READ_NULL_POINT\nE:FATAL:PWR_BUS_OVERLOAD</span>")
|
||||
to_chat(user, "<span class='userdanger'>An electromagnetic pulse disrupts your [name] and violently tears you out of time-bluespace!</span>")
|
||||
user.emote("scream")
|
||||
deactivate(1, 1)
|
||||
if(severity >= 70)
|
||||
if(activated && user && ishuman(user) && (user.wear_suit == src))
|
||||
to_chat(user, "<span class='danger'>E:FATAL:RAM_READ_FAIL\nE:FATAL:STACK_EMPTY\nE:FATAL:READ_NULL_POINT\nE:FATAL:PWR_BUS_OVERLOAD</span>")
|
||||
to_chat(user, "<span class='userdanger'>An electromagnetic pulse disrupts your [name] and violently tears you out of time-bluespace!</span>")
|
||||
user.emote("scream")
|
||||
deactivate(1, 1)
|
||||
|
||||
/obj/item/clothing/suit/space/chronos/proc/finish_chronowalk(mob/living/carbon/human/user, turf/to_turf)
|
||||
if(!user)
|
||||
|
||||
@@ -525,7 +525,7 @@ Contains:
|
||||
icon_state = "hunter"
|
||||
item_state = "swat_suit"
|
||||
allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals, /obj/item/kitchen/knife/combat)
|
||||
armor = list("melee" = 60, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100, "wpound" = 25)
|
||||
armor = list("melee" = 60, "bullet" = 40, "laser" = 40, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100, "wound" = 25)
|
||||
strip_delay = 130
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
|
||||
|
||||
@@ -80,7 +80,9 @@
|
||||
item_state = "hostrench"
|
||||
flags_inv = 0
|
||||
strip_delay = 80
|
||||
unique_reskin = list("Coat" = "hostrench", "Cloak" = "trenchcloak")
|
||||
unique_reskin = list("Coat" = "hostrench",
|
||||
"Cloak" = "trenchcloak"
|
||||
)
|
||||
|
||||
/obj/item/clothing/suit/armor/vest/warden
|
||||
name = "warden's jacket"
|
||||
|
||||
@@ -102,3 +102,20 @@
|
||||
/obj/item/clothing/neck/cloak/polychromic/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/polychromic, poly_colors, 3)
|
||||
|
||||
/obj/item/clothing/neck/cloak/alt
|
||||
name = "cloak"
|
||||
desc = "A ragged up white cloak. It reminds you of a place not far from here."
|
||||
icon_state = "cloak"
|
||||
item_state = "cloak"
|
||||
|
||||
/obj/item/clothing/neck/cloak/alt/polychromic
|
||||
name = "polychromic cloak"
|
||||
desc = "A ragged up cloak. It reminds you of a place not far from here."
|
||||
icon_state = "cloak"
|
||||
item_state = "cloak"
|
||||
var/list/poly_colors = list("#FFFFFF", "#676767", "#4C4C4C")
|
||||
|
||||
/obj/item/clothing/neck/cloak/alt/polychromic/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/polychromic, poly_colors, 3)
|
||||
|
||||
@@ -117,6 +117,27 @@
|
||||
icon_state = "greydet"
|
||||
item_state = "greydet"
|
||||
|
||||
/obj/item/clothing/suit/det_suit/forensicsred
|
||||
name = "red forensics coat"
|
||||
icon_state = "forensics_red"
|
||||
item_state = "forensics_red"
|
||||
|
||||
/obj/item/clothing/suit/det_suit/forensicsred/long
|
||||
name = "long red forensics coat"
|
||||
icon_state = "forensics_red_long"
|
||||
item_state = "forensics_red_long"
|
||||
|
||||
/obj/item/clothing/suit/det_suit/forensicsblue
|
||||
name = "blue forensics coat"
|
||||
icon_state = "forensics_blue"
|
||||
item_state = "forensics_blue"
|
||||
|
||||
/obj/item/clothing/suit/det_suit/forensicsblue/long
|
||||
name = "long blue forensics coat"
|
||||
icon_state = "forensics_blue_long"
|
||||
item_state = "forensics_blue_long"
|
||||
|
||||
|
||||
//Engineering
|
||||
/obj/item/clothing/suit/hazardvest
|
||||
name = "hazard vest"
|
||||
@@ -222,4 +243,16 @@
|
||||
desc = "A dark blue vest with reflective strips for emergency medical technicians."
|
||||
icon_state = "labcoat_emt"
|
||||
item_state = "labcoat_emt"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/emt/red
|
||||
name = "red emt vest"
|
||||
desc = "A red vest with reflective strips for emergency medical technicians."
|
||||
icon_state = "labcoat_emtred"
|
||||
item_state = "labcoat_emtred"
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/emt/highvis
|
||||
name = "high visibility emt vest"
|
||||
desc = "a high visibility vest for emergency medical technicians."
|
||||
icon_state = "labcoat_highvis"
|
||||
item_state = "labcoat_highvis"
|
||||
|
||||
@@ -62,3 +62,48 @@
|
||||
name = "scientist labcoat"
|
||||
desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder."
|
||||
icon_state = "labcoat_tox"
|
||||
|
||||
// Departmental Jackets
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/sci
|
||||
name = "science jacket"
|
||||
desc = "A comfortable jacket in science purple."
|
||||
icon_state = "sci_dep_jacket"
|
||||
item_state = "sci_dep_jacket"
|
||||
allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/med
|
||||
name = "medical jacket"
|
||||
desc = "A comfortable jacket in medical blue."
|
||||
icon_state = "med_dep_jacket"
|
||||
item_state = "med_dep_jacket"
|
||||
allowed = list(/obj/item/analyzer, /obj/item/sensor_device, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45)
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/sec
|
||||
name = "security jacket"
|
||||
desc = "A comfortable jacket in security red."
|
||||
icon_state = "sec_dep_jacket"
|
||||
item_state = "sec_dep_jacket"
|
||||
armor = list("melee" = 25, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45)
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/sup
|
||||
name = "supply jacket"
|
||||
desc = "A comfortable jacket in supply brown."
|
||||
icon_state = "supply_dep_jacket"
|
||||
item_state = "supply_dep_jacket"
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/sup/qm
|
||||
name = "quartermaster's jacket"
|
||||
desc = "A loose covering often warn by station quartermasters."
|
||||
icon_state = "qmjacket"
|
||||
item_state = "qmjacket"
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/eng
|
||||
name = "engineering jacket"
|
||||
desc = "A comfortable jacket in engineering yellow."
|
||||
icon_state = "engi_dep_jacket"
|
||||
item_state = "engi_dep_jacket"
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 45)
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/t_scanner, /obj/item/construction/rcd, /obj/item/pipe_dispenser, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user