Merge branch 'master' into upstream-merge-29839
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
// How multiple components of the exact same type are handled in the same datum
|
||||
|
||||
#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default)
|
||||
#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
|
||||
#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
|
||||
#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted
|
||||
|
||||
// All signals. Format:
|
||||
@@ -16,3 +16,9 @@
|
||||
#define COMSIG_PARENT_QDELETED "parent_qdeleted" //before a datum's Destroy() is called: ()
|
||||
#define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (atom/movable, atom)
|
||||
#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (atom/movable)
|
||||
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from the base of atom/attackby: (obj/item, mob/living, params)
|
||||
#define COMSIG_PARENT_EXAMINE "atom_examine" //from the base of atom/examine: (mob)
|
||||
#define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (atom/movable, atom)
|
||||
#define COMSIG_ATOM_EX_ACT "atom_ex_act" //from base of atom/ex_act(): (severity, target)
|
||||
#define COMSIG_ATOM_SING_PULL "atom_sing_pull" //from base of atom/singularity_pull(): (S, current_size)
|
||||
#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (atom/movable)
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#define islava(A) (istype(A, /turf/open/lava))
|
||||
|
||||
#define isplatingturf(A) (istype(A, /turf/open/floor/plating))
|
||||
|
||||
//Mobs
|
||||
#define isliving(A) (istype(A, /mob/living))
|
||||
|
||||
|
||||
@@ -453,3 +453,13 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
|
||||
#define MOUSE_OPACITY_TRANSPARENT 0
|
||||
#define MOUSE_OPACITY_ICON 1
|
||||
#define MOUSE_OPACITY_OPAQUE 2
|
||||
|
||||
//world/proc/shelleo
|
||||
#define SHELLEO_ERRORLEVEL 1
|
||||
#define SHELLEO_STDOUT 2
|
||||
#define SHELLEO_STDERR 3
|
||||
|
||||
//server security mode
|
||||
#define SECURITY_SAFE 1
|
||||
#define SECURITY_ULTRASAFE 2
|
||||
#define SECURITY_TRUSTED 3
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
//keep these in sync with TGS3
|
||||
#define SERVICE_WORLD_PARAM "server_service"
|
||||
#define SERVICE_PR_TEST_JSON "..\\..\\prtestjob.json"
|
||||
#define SERVICE_VERSION_PARAM "server_service_version"
|
||||
#define SERVICE_PR_TEST_JSON "prtestjob.json"
|
||||
#define SERVICE_PR_TEST_JSON_OLD "..\\..\\[SERVICE_PR_TEST_JSON]"
|
||||
|
||||
#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
|
||||
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
|
||||
|
||||
57
code/__HELPERS/shell.dm
Normal file
57
code/__HELPERS/shell.dm
Normal file
@@ -0,0 +1,57 @@
|
||||
//Runs the command in the system's shell, returns a list of (error code, stdout, stderr)
|
||||
|
||||
#define SHELLEO_NAME "data/shelleo."
|
||||
#define SHELLEO_ERR ".err"
|
||||
#define SHELLEO_OUT ".out"
|
||||
/world/proc/shelleo(command)
|
||||
var/static/list/shelleo_ids = list()
|
||||
var/stdout = ""
|
||||
var/stderr = ""
|
||||
var/errorcode = 1
|
||||
var/shelleo_id
|
||||
var/out_file = ""
|
||||
var/err_file = ""
|
||||
var/static/list/interpreters = list("[MS_WINDOWS]" = "cmd /c", "[UNIX]" = "sh -c")
|
||||
var/interpreter = interpreters["[world.system_type]"]
|
||||
if(interpreter)
|
||||
for(var/seo_id in shelleo_ids)
|
||||
if(!shelleo_ids[seo_id])
|
||||
shelleo_ids[seo_id] = TRUE
|
||||
shelleo_id = "[seo_id]"
|
||||
break
|
||||
if(!shelleo_id)
|
||||
shelleo_id = "[shelleo_ids.len + 1]"
|
||||
shelleo_ids += shelleo_id
|
||||
shelleo_ids[shelleo_id] = TRUE
|
||||
out_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_OUT]"
|
||||
err_file = "[SHELLEO_NAME][shelleo_id][SHELLEO_ERR]"
|
||||
errorcode = shell("[interpreter] \"[command]\" > [out_file] 2> [err_file]")
|
||||
if(fexists(out_file))
|
||||
stdout = file2text(out_file)
|
||||
fdel(out_file)
|
||||
if(fexists(err_file))
|
||||
stderr = file2text(err_file)
|
||||
fdel(err_file)
|
||||
shelleo_ids[shelleo_id] = FALSE
|
||||
else
|
||||
CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system
|
||||
. = list(errorcode, stdout, stderr)
|
||||
#undef SHELLEO_NAME
|
||||
#undef SHELLEO_ERR
|
||||
#undef SHELLEO_OUT
|
||||
|
||||
/proc/shell_url_scrub(url)
|
||||
var/static/regex/bad_chars_regex = regex("\[^#%&./:=?\\w]*", "g")
|
||||
var/scrubbed_url = ""
|
||||
var/bad_match = ""
|
||||
var/last_good = 1
|
||||
var/bad_chars = 1
|
||||
do
|
||||
bad_chars = bad_chars_regex.Find(url)
|
||||
scrubbed_url += copytext(url, last_good, bad_chars)
|
||||
if(bad_chars)
|
||||
bad_match = url_encode(bad_chars_regex.match)
|
||||
scrubbed_url += bad_match
|
||||
last_good = bad_chars + length(bad_match)
|
||||
while(bad_chars)
|
||||
. = scrubbed_url
|
||||
@@ -16,7 +16,7 @@ GLOBAL_PROTECT(config_dir)
|
||||
return ..()
|
||||
|
||||
/datum/configuration/vv_edit_var(var_name, var_value)
|
||||
var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank")
|
||||
var/static/list/banned_edits = list("cross_address", "cross_allowed", "autoadmin", "autoadmin_rank", "invoke_youtubedl")
|
||||
if(var_name in banned_edits)
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -94,6 +94,8 @@ GLOBAL_PROTECT(config_dir)
|
||||
var/panic_server_name
|
||||
var/panic_address //Reconnect a player this linked server if this server isn't accepting new players
|
||||
|
||||
var/invoke_youtubedl
|
||||
|
||||
//IP Intel vars
|
||||
var/ipintel_email
|
||||
var/ipintel_rating_bad = 1
|
||||
@@ -478,6 +480,8 @@ GLOBAL_PROTECT(config_dir)
|
||||
if("panic_server_address")
|
||||
if(value != "byond://address:port")
|
||||
panic_address = value
|
||||
if("invoke_youtubedl")
|
||||
invoke_youtubedl = value
|
||||
if("show_irc_name")
|
||||
showircname = 1
|
||||
if("see_own_notes")
|
||||
|
||||
92
code/datums/components/archaeology.dm
Normal file
92
code/datums/components/archaeology.dm
Normal file
@@ -0,0 +1,92 @@
|
||||
/datum/component/archaeology
|
||||
dupe_type = COMPONENT_DUPE_UNIQUE
|
||||
var/list/archdrops
|
||||
var/prob2drop
|
||||
var/dug
|
||||
|
||||
/datum/component/archaeology/Initialize(_prob2drop, list/_archdrops = list())
|
||||
prob2drop = Clamp(_prob2drop, 0, 100)
|
||||
archdrops = _archdrops
|
||||
RegisterSignal(COMSIG_PARENT_ATTACKBY,.proc/Dig)
|
||||
RegisterSignal(COMSIG_ATOM_EX_ACT, .proc/BombDig)
|
||||
RegisterSignal(COMSIG_ATOM_SING_PULL, .proc/SingDig)
|
||||
|
||||
/datum/component/archaeology/InheritComponent(datum/component/archaeology/A, i_am_original)
|
||||
var/list/other_archdrops = A.archdrops
|
||||
var/list/_archdrops = archdrops
|
||||
for(var/I in other_archdrops)
|
||||
_archdrops[I] += other_archdrops[I]
|
||||
|
||||
/datum/component/archaeology/proc/Dig(mob/user, obj/item/W)
|
||||
if(dug)
|
||||
to_chat(user, "<span class='notice'>Looks like someone has dug here already.</span>")
|
||||
return FALSE
|
||||
else
|
||||
var/digging_speed
|
||||
if (istype(W, /obj/item/shovel))
|
||||
var/obj/item/shovel/S = W
|
||||
digging_speed = S.digspeed
|
||||
else if (istype(W, /obj/item/pickaxe))
|
||||
var/obj/item/pickaxe/P = W
|
||||
digging_speed = P.digspeed
|
||||
|
||||
if (digging_speed && isturf(user.loc))
|
||||
to_chat(user, "<span class='notice'>You start digging...</span>")
|
||||
playsound(parent, 'sound/effects/shovel_dig.ogg', 50, 1)
|
||||
|
||||
if(do_after(user, digging_speed, target = parent))
|
||||
to_chat(user, "<span class='notice'>You dig a hole.</span>")
|
||||
gets_dug()
|
||||
dug = TRUE
|
||||
SSblackbox.add_details("pick_used_mining",W.type)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/component/archaeology/proc/gets_dug()
|
||||
if(dug)
|
||||
return
|
||||
else
|
||||
var/turf/open/OT = get_turf(parent)
|
||||
for(var/thing in archdrops)
|
||||
var/maxtodrop = archdrops[thing]
|
||||
for(var/i in 1 to maxtodrop)
|
||||
if(prob(prob2drop)) // can't win them all!
|
||||
new thing(OT)
|
||||
|
||||
if(isopenturf(OT))
|
||||
if(OT.postdig_icon_change)
|
||||
if(istype(OT, /turf/open/floor/plating/asteroid/) && !OT.postdig_icon)
|
||||
var/turf/open/floor/plating/asteroid/AOT = parent
|
||||
AOT.icon_plating = "[AOT.environment_type]_dug"
|
||||
AOT.icon_state = "[AOT.environment_type]_dug"
|
||||
else
|
||||
if(isplatingturf(OT))
|
||||
var/turf/open/floor/plating/POT = parent
|
||||
POT.icon_plating = "[POT.postdig_icon]"
|
||||
OT.icon_state = "[OT.postdig_icon]"
|
||||
|
||||
if(OT.slowdown) //Things like snow slow you down until you dig them.
|
||||
OT.slowdown = 0
|
||||
dug = TRUE
|
||||
|
||||
/datum/component/archaeology/proc/SingDig(S, current_size)
|
||||
switch(current_size)
|
||||
if(STAGE_THREE)
|
||||
if(prob(30))
|
||||
gets_dug()
|
||||
if(STAGE_FOUR)
|
||||
if(prob(50))
|
||||
gets_dug()
|
||||
else
|
||||
if(current_size >= STAGE_FIVE && prob(70))
|
||||
gets_dug()
|
||||
|
||||
/datum/component/archaeology/proc/BombDig(severity, target)
|
||||
switch(severity)
|
||||
if(3)
|
||||
return
|
||||
if(2)
|
||||
if(prob(20))
|
||||
gets_dug()
|
||||
if(1)
|
||||
gets_dug()
|
||||
@@ -22,46 +22,6 @@
|
||||
|
||||
//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down
|
||||
//add separaters by doing . += "---"
|
||||
/datum/proc/vv_get_dropdown()
|
||||
. = list()
|
||||
. += "---"
|
||||
.["Call Proc"] = "?_src_=vars;proc_call=\ref[src]"
|
||||
.["Mark Object"] = "?_src_=vars;mark_object=\ref[src]"
|
||||
.["Delete"] = "?_src_=vars;delete=\ref[src]"
|
||||
|
||||
|
||||
/datum/proc/on_reagent_change()
|
||||
return
|
||||
|
||||
|
||||
/client/proc/debug_variables(datum/D in world)
|
||||
set category = "Debug"
|
||||
set name = "View Variables"
|
||||
//set src in world
|
||||
var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round.
|
||||
|
||||
if(!usr.client || !usr.client.holder)
|
||||
to_chat(usr, "<span class='danger'>You need to be an administrator to access this.</span>")
|
||||
return
|
||||
|
||||
if(!D)
|
||||
return
|
||||
|
||||
var/islist = islist(D)
|
||||
if (!islist && !istype(D))
|
||||
return
|
||||
|
||||
var/title = ""
|
||||
var/refid = "\ref[D]"
|
||||
var/icon/sprite
|
||||
var/hash
|
||||
|
||||
var/type = /list
|
||||
if (!islist)
|
||||
type = D.type
|
||||
|
||||
|
||||
|
||||
/datum/proc/vv_get_dropdown()
|
||||
. = list()
|
||||
. += "---"
|
||||
@@ -103,14 +63,13 @@
|
||||
|
||||
|
||||
|
||||
>>>>>>> 84b1e3d... [s] Adds a security token to all admin hrefs (#29839)
|
||||
if(istype(D, /atom))
|
||||
var/atom/AT = D
|
||||
if(AT.icon && AT.icon_state)
|
||||
sprite = new /icon(AT.icon, AT.icon_state)
|
||||
hash = md5(AT.icon)
|
||||
hash = md5(hash + AT.icon_state)
|
||||
usr << browse_rsc(sprite, "vv[hash].png")
|
||||
src << browse_rsc(sprite, "vv[hash].png")
|
||||
|
||||
title = "[D] (\ref[D]) = [type]"
|
||||
|
||||
@@ -120,413 +79,6 @@
|
||||
var/list/atomsnowflake = list()
|
||||
|
||||
if(istype(D, /atom))
|
||||
var/atom/A = D
|
||||
if(isliving(A))
|
||||
atomsnowflake += "<a href='?_src_=vars;rename=[refid]'><b>[D]</b></a>"
|
||||
if(A.dir)
|
||||
atomsnowflake += "<br><font size='1'><a href='?_src_=vars;rotatedatum=[refid];rotatedir=left'><<</a> <a href='?_src_=vars;datumedit=[refid];varnameedit=dir'>[dir2text(A.dir)]</a> <a href='?_src_=vars;rotatedatum=[refid];rotatedir=right'>>></a></font>"
|
||||
var/mob/living/M = A
|
||||
atomsnowflake += {"
|
||||
<br><font size='1'><a href='?_src_=vars;datumedit=[refid];varnameedit=ckey'>[M.ckey ? M.ckey : "No ckey"]</a> / <a href='?_src_=vars;datumedit=[refid];varnameedit=real_name'>[M.real_name ? M.real_name : "No real name"]</a></font>
|
||||
<br><font size='1'>
|
||||
BRUTE:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=brute'>[M.getBruteLoss()]</a>
|
||||
FIRE:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=fire'>[M.getFireLoss()]</a>
|
||||
TOXIN:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=toxin'>[M.getToxLoss()]</a>
|
||||
OXY:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=oxygen'>[M.getOxyLoss()]</a>
|
||||
CLONE:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=clone'>[M.getCloneLoss()]</a>
|
||||
BRAIN:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=brain'>[M.getBrainLoss()]</a>
|
||||
STAMINA:<font size='1'><a href='?_src_=vars;mobToDamage=[refid];adjustDamage=stamina'>[M.getStaminaLoss()]</a>
|
||||
</font>
|
||||
"}
|
||||
else
|
||||
atomsnowflake += "<a href='?_src_=vars;datumedit=[refid];varnameedit=name'><b>[D]</b></a>"
|
||||
if(A.dir)
|
||||
atomsnowflake += "<br><font size='1'><a href='?_src_=vars;rotatedatum=[refid];rotatedir=left'><<</a> <a href='?_src_=vars;datumedit=[refid];varnameedit=dir'>[dir2text(A.dir)]</a> <a href='?_src_=vars;rotatedatum=[refid];rotatedir=right'>>></a></font>"
|
||||
else
|
||||
atomsnowflake += "<b>[D]</b>"
|
||||
|
||||
var/formatted_type = "[type]"
|
||||
if(length(formatted_type) > 25)
|
||||
var/middle_point = length(formatted_type) / 2
|
||||
var/splitpoint = findtext(formatted_type,"/",middle_point)
|
||||
if(splitpoint)
|
||||
formatted_type = "[copytext(formatted_type,1,splitpoint)]<br>[copytext(formatted_type,splitpoint)]"
|
||||
else
|
||||
formatted_type = "Type too long" //No suitable splitpoint (/) found.
|
||||
|
||||
var/marked
|
||||
if(holder.marked_datum && holder.marked_datum == D)
|
||||
marked = "<br><font size='1' color='red'><b>Marked Object</b></font>"
|
||||
var/varedited_line = ""
|
||||
if(!islist && D.var_edited)
|
||||
varedited_line = "<br><font size='1' color='red'><b>Var Edited</b></font>"
|
||||
|
||||
var/list/dropdownoptions = list()
|
||||
if (islist)
|
||||
dropdownoptions = list(
|
||||
"---",
|
||||
"Add Item" = "?_src_=vars;listadd=[refid]",
|
||||
"Remove Nulls" = "?_src_=vars;listnulls=[refid]",
|
||||
"Remove Dupes" = "?_src_=vars;listdupes=[refid]",
|
||||
"Set len" = "?_src_=vars;listlen=[refid]",
|
||||
"Shuffle" = "?_src_=vars;listshuffle=[refid]"
|
||||
)
|
||||
else
|
||||
dropdownoptions = D.vv_get_dropdown()
|
||||
var/list/dropdownoptions_html = list()
|
||||
|
||||
for (var/name in dropdownoptions)
|
||||
var/link = dropdownoptions[name]
|
||||
if (link)
|
||||
dropdownoptions_html += "<option value='[link]'>[name]</option>"
|
||||
else
|
||||
dropdownoptions_html += "<option value>[name]</option>"
|
||||
|
||||
var/list/names = list()
|
||||
if (!islist)
|
||||
for (var/V in D.vars)
|
||||
names += V
|
||||
sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects.
|
||||
|
||||
var/list/variable_html = list()
|
||||
if (islist)
|
||||
var/list/L = D
|
||||
for (var/i in 1 to L.len)
|
||||
var/key = L[i]
|
||||
var/value
|
||||
if (IS_NORMAL_LIST(L) && !isnum(key))
|
||||
value = L[key]
|
||||
variable_html += debug_variable(i, value, 0, D)
|
||||
else
|
||||
|
||||
names = sortList(names)
|
||||
for (var/V in names)
|
||||
if(D.can_vv_get(V))
|
||||
variable_html += D.vv_get_var(V)
|
||||
|
||||
var/html = {"
|
||||
<html>
|
||||
<head>
|
||||
<title>[title]</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Verdana, sans-serif;
|
||||
font-size: 9pt;
|
||||
}
|
||||
.value {
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 8pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload='selectTextField(); updateSearch()' onkeydown='return checkreload()' onkeyup='updateSearch()'>
|
||||
<script type="text/javascript">
|
||||
function checkreload() {
|
||||
if(event.keyCode == 116){ //F5 (to refresh properly)
|
||||
document.getElementById("refresh_link").click();
|
||||
event.preventDefault ? event.preventDefault() : (event.returnValue = false)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function updateSearch(){
|
||||
var filter_text = document.getElementById('filter');
|
||||
var filter = filter_text.value.toLowerCase();
|
||||
if(event.keyCode == 13){ //Enter / return
|
||||
var vars_ol = document.getElementById('vars');
|
||||
var lis = vars_ol.getElementsByTagName("li");
|
||||
for ( var i = 0; i < lis.length; ++i )
|
||||
{
|
||||
try{
|
||||
var li = lis\[i\];
|
||||
if ( li.style.backgroundColor == "#ffee88" )
|
||||
{
|
||||
alist = lis\[i\].getElementsByTagName("a")
|
||||
if(alist.length > 0){
|
||||
location.href=alist\[0\].href;
|
||||
}
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
return
|
||||
}
|
||||
if(event.keyCode == 38){ //Up arrow
|
||||
var vars_ol = document.getElementById('vars');
|
||||
var lis = vars_ol.getElementsByTagName("li");
|
||||
for ( var i = 0; i < lis.length; ++i )
|
||||
{
|
||||
try{
|
||||
var li = lis\[i\];
|
||||
if ( li.style.backgroundColor == "#ffee88" )
|
||||
{
|
||||
if( (i-1) >= 0){
|
||||
var li_new = lis\[i-1\];
|
||||
li.style.backgroundColor = "white";
|
||||
li_new.style.backgroundColor = "#ffee88";
|
||||
return
|
||||
}
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
return
|
||||
}
|
||||
if(event.keyCode == 40){ //Down arrow
|
||||
var vars_ol = document.getElementById('vars');
|
||||
var lis = vars_ol.getElementsByTagName("li");
|
||||
for ( var i = 0; i < lis.length; ++i )
|
||||
{
|
||||
try{
|
||||
var li = lis\[i\];
|
||||
if ( li.style.backgroundColor == "#ffee88" )
|
||||
{
|
||||
if( (i+1) < lis.length){
|
||||
var li_new = lis\[i+1\];
|
||||
li.style.backgroundColor = "white";
|
||||
li_new.style.backgroundColor = "#ffee88";
|
||||
return
|
||||
}
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//This part here resets everything to how it was at the start so the filter is applied to the complete list. Screw efficiency, it's client-side anyway and it only looks through 200 or so variables at maximum anyway (mobs).
|
||||
if(complete_list != null && complete_list != ""){
|
||||
var vars_ol1 = document.getElementById("vars");
|
||||
vars_ol1.innerHTML = complete_list
|
||||
}
|
||||
document.cookie="[refid][cookieoffset]search="+encodeURIComponent(filter);
|
||||
if(filter == ""){
|
||||
return;
|
||||
}else{
|
||||
var vars_ol = document.getElementById('vars');
|
||||
var lis = vars_ol.getElementsByTagName("li");
|
||||
for ( var i = 0; i < lis.length; ++i )
|
||||
{
|
||||
try{
|
||||
var li = lis\[i\];
|
||||
if ( li.innerText.toLowerCase().indexOf(filter) == -1 )
|
||||
{
|
||||
vars_ol.removeChild(li);
|
||||
i--;
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
}
|
||||
var lis_new = vars_ol.getElementsByTagName("li");
|
||||
for ( var j = 0; j < lis_new.length; ++j )
|
||||
{
|
||||
var li1 = lis\[j\];
|
||||
if (j == 0){
|
||||
li1.style.backgroundColor = "#ffee88";
|
||||
}else{
|
||||
li1.style.backgroundColor = "white";
|
||||
}
|
||||
}
|
||||
}
|
||||
function selectTextField() {
|
||||
var filter_text = document.getElementById('filter');
|
||||
filter_text.focus();
|
||||
filter_text.select();
|
||||
var lastsearch = getCookie("[refid][cookieoffset]search");
|
||||
if (lastsearch) {
|
||||
filter_text.value = lastsearch;
|
||||
updateSearch();
|
||||
}
|
||||
}
|
||||
function loadPage(list) {
|
||||
if(list.options\[list.selectedIndex\].value == ""){
|
||||
return;
|
||||
}
|
||||
location.href=list.options\[list.selectedIndex\].value;
|
||||
}
|
||||
function getCookie(cname) {
|
||||
var name = cname + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i=0; i<ca.length; i++) {
|
||||
var c = ca\[i\];
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
</script>
|
||||
<div align='center'>
|
||||
<table width='100%'>
|
||||
<tr>
|
||||
<td width='50%'>
|
||||
<table align='center' width='100%'>
|
||||
<tr>
|
||||
<td>
|
||||
[sprite_text]
|
||||
<div align='center'>
|
||||
[atomsnowflake.Join()]
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div align='center'>
|
||||
<b><font size='1'>[formatted_type]</font></b>
|
||||
[marked]
|
||||
[varedited_line]
|
||||
</div>
|
||||
</td>
|
||||
<td width='50%'>
|
||||
<div align='center'>
|
||||
<a id='refresh_link' href='?_src_=vars;datumrefresh=[refid]'>Refresh</a>
|
||||
<form>
|
||||
<select name="file" size="1"
|
||||
onchange="loadPage(this.form.elements\[0\])"
|
||||
target="_parent._top"
|
||||
onmouseclick="this.focus()"
|
||||
style="background-color:#ffffff">
|
||||
<option value selected>Select option</option>
|
||||
[dropdownoptions_html.Join()]
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<hr>
|
||||
<font size='1'>
|
||||
<b>E</b> - Edit, tries to determine the variable type by itself.<br>
|
||||
<b>C</b> - Change, asks you for the var type first.<br>
|
||||
<b>M</b> - Mass modify: changes this variable for all objects of this type.<br>
|
||||
</font>
|
||||
<hr>
|
||||
<table width='100%'>
|
||||
<tr>
|
||||
<td width='20%'>
|
||||
<div align='center'>
|
||||
<b>Search:</b>
|
||||
</div>
|
||||
</td>
|
||||
<td width='80%'>
|
||||
<input type='text' id='filter' name='filter_text' value='' style='width:100%;'>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<ol id='vars'>
|
||||
[variable_html.Join()]
|
||||
</ol>
|
||||
<script type='text/javascript'>
|
||||
var vars_ol = document.getElementById("vars");
|
||||
var complete_list = vars_ol.innerHTML;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"}
|
||||
|
||||
usr << browse(html, "window=variables[refid];size=475x650")
|
||||
|
||||
|
||||
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
|
||||
/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE)
|
||||
var/header
|
||||
if(DA)
|
||||
if (islist(DA))
|
||||
var/index = name
|
||||
if (value)
|
||||
name = DA[name] //name is really the index until this line
|
||||
else
|
||||
value = DA[name]
|
||||
header = "<li style='backgroundColor:white'>(<a href='?_src_=vars;listedit=\ref[DA];index=[index]'>E</a>) (<a href='?_src_=vars;listchange=\ref[DA];index=[index]'>C</a>) (<a href='?_src_=vars;listremove=\ref[DA];index=[index]'>-</a>) "
|
||||
else
|
||||
header = "<li style='backgroundColor:white'>(<a href='?_src_=vars;datumedit=\ref[DA];varnameedit=[name]'>E</a>) (<a href='?_src_=vars;datumchange=\ref[DA];varnamechange=[name]'>C</a>) (<a href='?_src_=vars;datummass=\ref[DA];varnamemass=[name]'>M</a>) "
|
||||
else
|
||||
header = "<li>"
|
||||
|
||||
var/item
|
||||
if (isnull(value))
|
||||
item = "[VV_HTML_ENCODE(name)] = <span class='value'>null</span>"
|
||||
|
||||
else if (istext(value))
|
||||
item = "[VV_HTML_ENCODE(name)] = <span class='value'>\"[VV_HTML_ENCODE(value)]\"</span>"
|
||||
|
||||
else if (isicon(value))
|
||||
#ifdef VARSICON
|
||||
var/icon/I = new/icon(value)
|
||||
var/rnd = rand(1,10000)
|
||||
var/rname = "tmp\ref[I][rnd].png"
|
||||
usr << browse_rsc(I, rname)
|
||||
item = "[VV_HTML_ENCODE(name)] = (<span class='value'>[value]</span>) <img class=icon src=\"[rname]\">"
|
||||
#else
|
||||
item = "[VV_HTML_ENCODE(name)] = /icon (<span class='value'>[value]</span>)"
|
||||
#endif
|
||||
|
||||
/* else if (istype(value, /image))
|
||||
#ifdef VARSICON
|
||||
var/rnd = rand(1, 10000)
|
||||
var/image/I = value
|
||||
|
||||
src << browse_rsc(I.icon, "tmp\ref[value][rnd].png")
|
||||
html += "[name] = <img src=\"tmp\ref[value][rnd].png\">"
|
||||
#else
|
||||
html += "[name] = /image (<span class='value'>[value]</span>)"
|
||||
#endif
|
||||
*/
|
||||
else if (isfile(value))
|
||||
item = "[VV_HTML_ENCODE(name)] = <span class='value'>'[value]'</span>"
|
||||
|
||||
//else if (istype(value, /client))
|
||||
// var/client/C = value
|
||||
// item = "<a href='?_src_=vars;Vars=\ref[value]'>[VV_HTML_ENCODE(name)] \ref[value]</a> = [C] [C.type]"
|
||||
|
||||
else if (istype(value, /datum))
|
||||
var/datum/D = value
|
||||
if ("[D]" != "[D.type]") //if the thing as a name var, lets use it.
|
||||
item = "<a href='?_src_=vars;Vars=\ref[value]'>[VV_HTML_ENCODE(name)] \ref[value]</a> = [D] [D.type]"
|
||||
else
|
||||
item = "<a href='?_src_=vars;Vars=\ref[value]'>[VV_HTML_ENCODE(name)] \ref[value]</a> = [D.type]"
|
||||
|
||||
else if (islist(value))
|
||||
var/list/L = value
|
||||
var/list/items = list()
|
||||
|
||||
if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150)))
|
||||
for (var/i in 1 to L.len)
|
||||
var/key = L[i]
|
||||
var/val
|
||||
if (IS_NORMAL_LIST(L) && !isnum(key))
|
||||
val = L[key]
|
||||
if (!val)
|
||||
val = key
|
||||
key = i
|
||||
|
||||
items += debug_variable(key, val, level + 1, sanitize = sanitize)
|
||||
|
||||
item = "<a href='?_src_=vars;Vars=\ref[value]'>[VV_HTML_ENCODE(name)] = /list ([L.len])</a><ul>[items.Join()]</ul>"
|
||||
else
|
||||
item = "<a href='?_src_=vars;Vars=\ref[value]'>[VV_HTML_ENCODE(name)] = /list ([L.len])</a>"
|
||||
|
||||
else
|
||||
item = "[VV_HTML_ENCODE(name)] = <span class='value'>[VV_HTML_ENCODE(value)]</span>"
|
||||
|
||||
return "[header][item]</li>"
|
||||
|
||||
#undef VV_HTML_ENCODE
|
||||
|
||||
/client/proc/view_var_Topic(href, href_list, hsrc)
|
||||
if( (usr.client != src) || !src.holder )
|
||||
return
|
||||
if(href_list["Vars"])
|
||||
debug_variables(locate(href_list["Vars"]))
|
||||
|
||||
else if(href_list["datumrefresh"])
|
||||
var/datum/DAT = locate(href_list["datumrefresh"])
|
||||
if(!DAT) //can't be an istype() because /client etc aren't datums
|
||||
return
|
||||
src.debug_variables(DAT)
|
||||
|
||||
else if(href_list["mob_player_panel"])
|
||||
if(!check_rights(0))
|
||||
return
|
||||
|
||||
var/atom/A = D
|
||||
if(isliving(A))
|
||||
atomsnowflake += "<a href='?_src_=vars;[HrefToken()];rename=[refid]'><b>[D]</b></a>"
|
||||
@@ -934,7 +486,6 @@
|
||||
if(!check_rights(0))
|
||||
return
|
||||
|
||||
>>>>>>> 84b1e3d... [s] Adds a security token to all admin hrefs (#29839)
|
||||
var/mob/M = locate(href_list["mob_player_panel"]) in GLOB.mob_list
|
||||
if(!istype(M))
|
||||
to_chat(usr, "This can only be used on instances of type /mob")
|
||||
@@ -995,22 +546,6 @@
|
||||
to_chat(usr, "This can only be done to instances of type /mob")
|
||||
return
|
||||
M.regenerate_icons()
|
||||
|
||||
//Needs +VAREDIT past this point
|
||||
|
||||
else if(check_rights(R_VAREDIT))
|
||||
|
||||
|
||||
//~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
|
||||
|
||||
if(href_list["rename"])
|
||||
if(!check_rights(0))
|
||||
return
|
||||
|
||||
if(!ismob(M))
|
||||
to_chat(usr, "This can only be done to instances of type /mob")
|
||||
return
|
||||
M.regenerate_icons()
|
||||
else if(href_list["expose"])
|
||||
if(!check_rights(R_ADMIN, FALSE))
|
||||
return
|
||||
@@ -1043,7 +578,6 @@
|
||||
if(!check_rights(0))
|
||||
return
|
||||
|
||||
>>>>>>> 84b1e3d... [s] Adds a security token to all admin hrefs (#29839)
|
||||
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")
|
||||
|
||||
@@ -6,8 +6,14 @@
|
||||
var/date
|
||||
|
||||
/datum/getrev/New()
|
||||
if(world.RunningService() && fexists(SERVICE_PR_TEST_JSON))
|
||||
testmerge = json_decode(file2text(SERVICE_PR_TEST_JSON))
|
||||
if(world.RunningService())
|
||||
var/file_name
|
||||
if(ServiceVersion()) //will return null for versions < 3.0.91.0
|
||||
file_name = SERVICE_PR_TEST_JSON_OLD
|
||||
else
|
||||
file_name = SERVICE_PR_TEST_JSON
|
||||
if(fexists(file_name))
|
||||
testmerge = json_decode(file2text(file_name))
|
||||
#ifdef SERVERTOOLS
|
||||
else if(!world.RunningService() && fexists("../prtestjob.lk")) //tgs2 support
|
||||
var/list/tmp = world.file2list("..\\prtestjob.lk")
|
||||
|
||||
@@ -304,6 +304,7 @@
|
||||
/atom/proc/ex_act(severity, target)
|
||||
set waitfor = FALSE
|
||||
contents_explosion(severity, target)
|
||||
SendSignal(COMSIG_ATOM_EX_ACT, severity, target)
|
||||
|
||||
/atom/proc/blob_act(obj/structure/blob/B)
|
||||
return
|
||||
@@ -468,8 +469,8 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
|
||||
/atom/proc/singularity_act()
|
||||
return
|
||||
|
||||
/atom/proc/singularity_pull()
|
||||
return
|
||||
/atom/proc/singularity_pull(obj/singularity/S, current_size)
|
||||
SendSignal(COMSIG_ATOM_SING_PULL, S, current_size)
|
||||
|
||||
/atom/proc/acid_act(acidpwr, acid_volume)
|
||||
return
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
//to differentiate it, naturally everyone forgot about this immediately and so some things
|
||||
//would bump twice, so now it's called Collide
|
||||
/atom/movable/proc/Collide(atom/A)
|
||||
if((A))
|
||||
if(A)
|
||||
if(throwing)
|
||||
throwing.hit_atom(A)
|
||||
. = 1
|
||||
|
||||
@@ -363,7 +363,7 @@
|
||||
to_chat(src, "<i>Node Blobs</i> are blobs which grow, like the core. Like the core it can activate resource and factory blobs.")
|
||||
to_chat(src, "<b>In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.</b>")
|
||||
to_chat(src, "<b>Shortcuts:</b> Click = Expand Blob <b>|</b> Middle Mouse Click = Rally Spores <b>|</b> Ctrl Click = Create Shield Blob <b>|</b> Alt Click = Remove Blob")
|
||||
to_chat(src, "Attempting to talk will send a message to all other GLOB.overminds, allowing you to coordinate with them.")
|
||||
to_chat(src, "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them.")
|
||||
if(!placed && autoplace_max_time <= world.time)
|
||||
to_chat(src, "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>")
|
||||
to_chat(src, "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.</font></span>")
|
||||
|
||||
@@ -90,8 +90,10 @@
|
||||
/obj/structure/blob/proc/Life()
|
||||
return
|
||||
|
||||
/obj/structure/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
|
||||
src.Be_Pulsed()
|
||||
/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
|
||||
if(QDELETED(pulsing_overmind))
|
||||
pulsing_overmind = overmind
|
||||
Be_Pulsed()
|
||||
var/expanded = FALSE
|
||||
if(prob(70) && expand())
|
||||
expanded = TRUE
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
to_chat(user, "<span class='inathneq'>An emergency shuttle has arrived and this prism is no longer useful; attempt to activate it to gain a partial refund of components used.</span>")
|
||||
else
|
||||
var/efficiency = get_efficiency_mod(TRUE)
|
||||
to_chat(user, "<span class='inathneq_small'>It requires at least <b>[get_delay_cost()]W</b> of power to attempt to delay the arrival of an emergency shuttle by <b>[2 * efficiency]</b> minutes.</span>")
|
||||
to_chat(user, "<span class='inathneq_small'>This cost increases by <b>[delay_cost_increase]W</b> for every previous activation.</span>")
|
||||
to_chat(user, "<span class='inathneq_small'>It requires at least <b>[DisplayPower(get_delay_cost())]</b> of power to attempt to delay the arrival of an emergency shuttle by <b>[2 * efficiency]</b> minutes.</span>")
|
||||
to_chat(user, "<span class='inathneq_small'>This cost increases by <b>[DisplayPower(delay_cost_increase)]</b> for every previous activation.</span>")
|
||||
|
||||
/obj/structure/destructible/clockwork/powered/prolonging_prism/forced_disable(bad_effects)
|
||||
if(active)
|
||||
@@ -126,7 +126,7 @@
|
||||
if(!hex_combo)
|
||||
hex_combo = mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER)
|
||||
else
|
||||
hex_combo.overlays += mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER)
|
||||
hex_combo.add_overlay(mutable_appearance('icons/effects/64x64.dmi', n, RIPPLE_LAYER))
|
||||
if(hex_combo) //YOU BUILT A HEXAGON
|
||||
hex_combo.pixel_x = -16
|
||||
hex_combo.pixel_y = -16
|
||||
|
||||
@@ -672,7 +672,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
|
||||
var/obj/machinery/transformer/conveyor = new(T)
|
||||
conveyor.masterAI = owner
|
||||
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
|
||||
owner_AI.can_shunt = TRUE
|
||||
owner_AI.can_shunt = FALSE
|
||||
to_chat(owner, "<span class='warning'>You are no longer able to shunt your core to APCs.</span>")
|
||||
adjust_uses(-1)
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
playsound(loc, 'sound/effects/glassbr3.ogg', 100, 1)
|
||||
stat |= BROKEN
|
||||
update_icon()
|
||||
set_light(0)
|
||||
|
||||
/obj/machinery/computer/emp_act(severity)
|
||||
switch(severity)
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
sleep(6)
|
||||
operating = FALSE
|
||||
desc += "<BR><span class='warning'>Its access panel is smoking slightly.</span>"
|
||||
open()
|
||||
open(2)
|
||||
|
||||
/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params)
|
||||
|
||||
|
||||
@@ -58,7 +58,9 @@
|
||||
/turf/open/floor/plating/asteroid/drill_act(obj/item/mecha_parts/mecha_equipment/drill/drill)
|
||||
for(var/turf/open/floor/plating/asteroid/M in range(1, drill.chassis))
|
||||
if(get_dir(drill.chassis,M)&drill.chassis.dir)
|
||||
M.gets_dug()
|
||||
for(var/I in GetComponents(/datum/component/archaeology))
|
||||
var/datum/component/archaeology/archy = I
|
||||
archy.gets_dug()
|
||||
drill.log_message("Drilled through [src]")
|
||||
drill.move_ores()
|
||||
|
||||
|
||||
@@ -549,9 +549,10 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
transfer_blood = 0
|
||||
|
||||
/obj/item/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FOUR)
|
||||
throw_at(S,14,3, spin=0)
|
||||
else ..()
|
||||
else return
|
||||
|
||||
/obj/item/throw_impact(atom/A)
|
||||
if(A && !QDELETED(A))
|
||||
|
||||
@@ -428,7 +428,7 @@
|
||||
|
||||
/obj/item/device/flashlight/glowstick/update_icon()
|
||||
item_state = "glowstick"
|
||||
overlays.Cut()
|
||||
cut_overlays()
|
||||
if(!fuel)
|
||||
icon_state = "glowstick-empty"
|
||||
cut_overlays()
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
icon_state = null
|
||||
active = TRUE
|
||||
if(tile_overlay)
|
||||
loc.overlays += tile_overlay
|
||||
loc.add_overlay(tile_overlay)
|
||||
else
|
||||
if(crossed)
|
||||
trigger() //no cheesing.
|
||||
|
||||
@@ -1,27 +1,201 @@
|
||||
/obj/item/banner
|
||||
name = "banner"
|
||||
desc = "A banner with Nanotrasen's logo on it."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "banner"
|
||||
item_state = "banner"
|
||||
force = 8
|
||||
attack_verb = list("forcefully inspired", "violently encouraged", "relentlessly galvanized")
|
||||
lefthand_file = 'icons/mob/inhands/equipment/banners_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/banners_righthand.dmi'
|
||||
desc = "A banner with Nanotrasen's logo on it."
|
||||
var/moralecooldown = 0
|
||||
var/moralewait = 600
|
||||
var/inspiration_available = TRUE //If this banner can be used to inspire crew
|
||||
var/morale_time = 0
|
||||
var/morale_cooldown = 600 //How many deciseconds between uses
|
||||
var/list/job_loyalties //Mobs with any of these assigned roles will be inspired
|
||||
var/list/role_loyalties //Mobs with any of these special roles will be inspired
|
||||
var/warcry
|
||||
|
||||
/obj/item/banner/examine(mob/user)
|
||||
..()
|
||||
if(inspiration_available)
|
||||
to_chat(user, "<span class='notice'>Activate it in your hand to inspire nearby allies of this banner's allegiance!</span>")
|
||||
|
||||
/obj/item/banner/attack_self(mob/living/carbon/human/user)
|
||||
if(moralecooldown + moralewait > world.time)
|
||||
if(!inspiration_available)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You increase the morale of your fellows!</span>")
|
||||
moralecooldown = world.time
|
||||
if(morale_time > world.time)
|
||||
to_chat(user, "<span class='warning'>You aren't feeling inspired enough to flourish [src] again yet.</span>")
|
||||
return
|
||||
user.visible_message("<span class='big notice'>[user] flourishes [src]!</span>", \
|
||||
"<span class='notice'>You raise [src] skywards, inspiring your allies!</span>")
|
||||
playsound(src, "rustle", 100, FALSE)
|
||||
if(warcry)
|
||||
user.say("[warcry]")
|
||||
var/old_transform = user.transform
|
||||
user.transform *= 1.2
|
||||
animate(user, transform = old_transform, time = 10)
|
||||
morale_time = world.time + morale_cooldown
|
||||
|
||||
for(var/mob/living/carbon/human/H in range(4,get_turf(src)))
|
||||
to_chat(H, "<span class='notice'>Your morale is increased by [user]'s banner!</span>")
|
||||
H.adjustBruteLoss(-15)
|
||||
H.adjustFireLoss(-15)
|
||||
H.AdjustStun(-40)
|
||||
H.AdjustKnockdown(-40)
|
||||
H.AdjustUnconscious(-40)
|
||||
var/list/inspired = list()
|
||||
var/has_job_loyalties = LAZYLEN(job_loyalties)
|
||||
var/has_role_loyalties = LAZYLEN(role_loyalties)
|
||||
inspired += user //The user is always inspired, regardless of loyalties
|
||||
for(var/mob/living/carbon/human/H in range(4, get_turf(src)))
|
||||
if(H.stat == DEAD || H == user)
|
||||
continue
|
||||
if(H.mind && (has_job_loyalties || has_role_loyalties))
|
||||
if(has_job_loyalties && H.mind.assigned_role in job_loyalties)
|
||||
inspired += H
|
||||
else if(has_role_loyalties && H.mind.special_role in role_loyalties)
|
||||
inspired += H
|
||||
else if(check_inspiration(H))
|
||||
inspired += H
|
||||
|
||||
for(var/V in inspired)
|
||||
var/mob/living/carbon/human/H = V
|
||||
if(H != user)
|
||||
to_chat(H, "<span class='notice'>Your confidence surges as [user] flourishes [user.p_their()] [name]!</span>")
|
||||
inspiration(H)
|
||||
special_inspiration(H)
|
||||
|
||||
/obj/item/banner/proc/check_inspiration(mob/living/carbon/human/H) //Banner-specific conditions for being eligible
|
||||
return
|
||||
|
||||
/obj/item/banner/proc/inspiration(mob/living/carbon/human/H)
|
||||
H.adjustBruteLoss(-15)
|
||||
H.adjustFireLoss(-15)
|
||||
H.AdjustStun(-40)
|
||||
H.AdjustKnockdown(-40)
|
||||
H.AdjustUnconscious(-40)
|
||||
playsound(H, 'sound/magic/staff_healing.ogg', 25, FALSE)
|
||||
|
||||
/obj/item/banner/proc/special_inspiration(mob/living/carbon/human/H) //Any banner-specific inspiration effects go here
|
||||
return
|
||||
|
||||
/obj/item/banner/security
|
||||
name = "securistan banner"
|
||||
desc = "The banner of Securistan, ruling the station with an iron fist."
|
||||
icon_state = "banner_security"
|
||||
job_loyalties = list("Security Officer", "Warden", "Detective", "Head of Security")
|
||||
warcry = "EVERYONE DOWN ON THE GROUND!!"
|
||||
|
||||
/obj/item/banner/security/mundane
|
||||
inspiration_available = FALSE
|
||||
|
||||
/datum/crafting_recipe/security_banner
|
||||
name = "Securistan Banner"
|
||||
result = /obj/item/banner/security/mundane
|
||||
time = 40
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/security = 1)
|
||||
category = CAT_MISC
|
||||
|
||||
/obj/item/banner/medical
|
||||
name = "meditopia banner"
|
||||
desc = "The banner of Meditopia, generous benefactors that cure wounds and shelter the weak."
|
||||
icon_state = "banner_medical"
|
||||
job_loyalties = list("Medical Doctor", "Chemist", "Geneticist", "Virologist", "Chief Medical Officer")
|
||||
warcry = "No wounds cannot be healed!"
|
||||
|
||||
/obj/item/banner/medical/mundane
|
||||
inspiration_available = FALSE
|
||||
|
||||
/obj/item/banner/medical/check_inspiration(mob/living/carbon/human/H)
|
||||
return H.stat //Meditopia is moved to help those in need
|
||||
|
||||
/datum/crafting_recipe/medical_banner
|
||||
name = "Meditopia Banner"
|
||||
result = /obj/item/banner/medical/mundane
|
||||
time = 40
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/medical = 1)
|
||||
category = CAT_MISC
|
||||
|
||||
/obj/item/banner/medical/special_inspiration(mob/living/carbon/human/H)
|
||||
H.adjustToxLoss(-15)
|
||||
H.setOxyLoss(0)
|
||||
H.reagents.add_reagent("inaprovaline", 5)
|
||||
|
||||
/obj/item/banner/science
|
||||
name = "sciencia banner"
|
||||
desc = "The banner of Sciencia, bold and daring thaumaturges and researchers that take the path less traveled."
|
||||
icon_state = "banner_science"
|
||||
job_loyalties = list("Scientist", "Roboticist", "Research Director")
|
||||
warcry = "For Cuban Pete!"
|
||||
|
||||
/obj/item/banner/science/mundane
|
||||
inspiration_available = FALSE
|
||||
|
||||
/obj/item/banner/science/check_inspiration(mob/living/carbon/human/H)
|
||||
return H.on_fire //Sciencia is pleased by dedication to the art of Toxins
|
||||
|
||||
/datum/crafting_recipe/science_banner
|
||||
name = "Sciencia Banner"
|
||||
result = /obj/item/banner/science/mundane
|
||||
time = 40
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/scientist = 1)
|
||||
category = CAT_MISC
|
||||
|
||||
/obj/item/banner/cargo
|
||||
name = "cargonia banner"
|
||||
desc = "The banner of the eternal Cargonia, with the mystical power of conjuring any object into existence."
|
||||
icon_state = "banner_cargo"
|
||||
job_loyalties = list("Cargo Technician", "Shaft Miner", "Quartermaster")
|
||||
warcry = "Hail Cargonia!"
|
||||
|
||||
/obj/item/banner/cargo/mundane
|
||||
inspiration_available = FALSE
|
||||
|
||||
/datum/crafting_recipe/cargo_banner
|
||||
name = "Cargonia Banner"
|
||||
result = /obj/item/banner/cargo/mundane
|
||||
time = 40
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/cargotech = 1)
|
||||
category = CAT_MISC
|
||||
|
||||
/obj/item/banner/engineering
|
||||
name = "engitopia banner"
|
||||
desc = "The banner of Engitopia, wielders of limitless power."
|
||||
icon_state = "banner_engineering"
|
||||
job_loyalties = list("Station Engineer", "Atmospheric Technician", "Chief Engineer")
|
||||
warcry = "All hail lord Singuloth!!"
|
||||
|
||||
/obj/item/banner/engineering/mundane
|
||||
inspiration_available = FALSE
|
||||
|
||||
/obj/item/banner/engineering/special_inspiration(mob/living/carbon/human/H)
|
||||
H.radiation = 0
|
||||
|
||||
/datum/crafting_recipe/engineering_banner
|
||||
name = "Engitopia Banner"
|
||||
result = /obj/item/banner/engineering/mundane
|
||||
time = 40
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/engineer = 1)
|
||||
category = CAT_MISC
|
||||
|
||||
/obj/item/banner/command
|
||||
name = "command banner"
|
||||
desc = "The banner of Command, a staunch and ancient line of bueraucratic kings and queens."
|
||||
//No icon state here since the default one is the NT banner
|
||||
job_loyalties = list("Captain", "Head of Personnel", "Chief Engineer", "Head of Security", "Research Director", "Chief Medical Officer")
|
||||
warcry = "Hail Nanotrasen!"
|
||||
|
||||
/obj/item/banner/command/mundane
|
||||
inspiration_available = FALSE
|
||||
|
||||
/obj/item/banner/command/check_inspiration(mob/living/carbon/human/H)
|
||||
return H.isloyal() //Command is stalwart but rewards their allies.
|
||||
|
||||
/datum/crafting_recipe/command_banner
|
||||
name = "Command Banner"
|
||||
result = /obj/item/banner/command/mundane
|
||||
time = 40
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/captainparade = 1)
|
||||
category = CAT_MISC
|
||||
|
||||
/obj/item/banner/red
|
||||
name = "red banner"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -169,6 +169,7 @@
|
||||
return
|
||||
|
||||
/obj/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(!anchored || current_size >= STAGE_FIVE)
|
||||
step_towards(src,S)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
var/height = 0 //the 'height' of the ladder. higher numbers are considered physically higher
|
||||
var/obj/structure/ladder/down = null //the ladder below this one
|
||||
var/obj/structure/ladder/up = null //the ladder above this one
|
||||
var/auto_connect = FALSE
|
||||
|
||||
/obj/structure/ladder/unbreakable //mostly useful for awaymissions to prevent halting progress in a mission
|
||||
name = "sturdy ladder"
|
||||
@@ -19,12 +20,18 @@
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/structure/ladder/Destroy()
|
||||
if(up && up.down == src)
|
||||
up.down = null
|
||||
up.update_icon()
|
||||
if(down && down.up == src)
|
||||
down.up = null
|
||||
down.update_icon()
|
||||
GLOB.ladders -= src
|
||||
. = ..()
|
||||
|
||||
/obj/structure/ladder/LateInitialize()
|
||||
for(var/obj/structure/ladder/L in GLOB.ladders)
|
||||
if(L.id == id)
|
||||
if(L.id == id || (auto_connect && L.auto_connect && L.x == x && L.y == y))
|
||||
if(L.height == (height - 1))
|
||||
down = L
|
||||
continue
|
||||
@@ -50,31 +57,33 @@
|
||||
else //wtf make your ladders properly assholes
|
||||
icon_state = "ladder00"
|
||||
|
||||
/obj/structure/ladder/proc/go_up(mob/user,is_ghost)
|
||||
/obj/structure/ladder/proc/travel(going_up, mob/user, is_ghost, obj/structure/ladder/ladder)
|
||||
if(!is_ghost)
|
||||
show_fluff_message(1,user)
|
||||
up.add_fingerprint(user)
|
||||
user.loc = get_turf(up)
|
||||
show_fluff_message(going_up,user)
|
||||
ladder.add_fingerprint(user)
|
||||
|
||||
var/atom/movable/AM
|
||||
if(user.pulling)
|
||||
AM = user.pulling
|
||||
user.pulling.forceMove(get_turf(ladder))
|
||||
user.forceMove(get_turf(ladder))
|
||||
if(AM)
|
||||
user.start_pulling(AM)
|
||||
|
||||
/obj/structure/ladder/proc/go_down(mob/user,is_ghost)
|
||||
if(!is_ghost)
|
||||
show_fluff_message(0,user)
|
||||
down.add_fingerprint(user)
|
||||
user.loc = get_turf(down)
|
||||
|
||||
/obj/structure/ladder/proc/use(mob/user,is_ghost=0)
|
||||
if(up && down)
|
||||
switch( alert("Go up or down the ladder?", "Ladder", "Up", "Down", "Cancel") )
|
||||
if("Up")
|
||||
go_up(user,is_ghost)
|
||||
travel(TRUE, user, is_ghost, up)
|
||||
if("Down")
|
||||
go_down(user,is_ghost)
|
||||
travel(FALSE, user, is_ghost, down)
|
||||
if("Cancel")
|
||||
return
|
||||
else if(up)
|
||||
go_up(user,is_ghost)
|
||||
travel(TRUE, user, is_ghost, up)
|
||||
else if(down)
|
||||
go_down(user,is_ghost)
|
||||
travel(FALSE, user,is_ghost, down)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] doesn't seem to lead anywhere!</span>")
|
||||
|
||||
@@ -108,3 +117,14 @@
|
||||
. = ..()
|
||||
else
|
||||
return QDEL_HINT_LETMELIVE
|
||||
|
||||
/obj/structure/ladder/unbreakable/singularity_pull()
|
||||
return
|
||||
|
||||
/obj/structure/ladder/auto_connect //They will connect to ladders with the same X and Y without needing to share an ID
|
||||
auto_connect = TRUE
|
||||
|
||||
|
||||
/obj/structure/ladder/singularity_pull()
|
||||
visible_message("<span class='danger'>[src] is torn to pieces by the gravitational pull!</span>")
|
||||
qdel(src)
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/lattice/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FOUR)
|
||||
deconstruct()
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/structure/transit_tube/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct(FALSE)
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
AM.ex_act(severity, target)
|
||||
|
||||
/obj/structure/transit_tube_pod/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct(FALSE)
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/window/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct(FALSE)
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
var/wet = 0
|
||||
var/wet_time = 0 // Time in seconds that this floor will be wet for.
|
||||
var/mutable_appearance/wet_overlay
|
||||
var/postdig_icon_change = FALSE
|
||||
var/postdig_icon
|
||||
var/list/archdrops
|
||||
|
||||
/turf/open/indestructible
|
||||
name = "floor"
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
return make_plating()
|
||||
|
||||
/turf/open/floor/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size == STAGE_THREE)
|
||||
if(prob(30))
|
||||
if(floor_tile)
|
||||
|
||||
@@ -269,6 +269,7 @@
|
||||
narsie_act(force, ignore_mobs, probability)
|
||||
|
||||
/turf/open/floor/vines/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
if(prob(50))
|
||||
ChangeTurf(src.baseturf)
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
icon = 'icons/turf/floors.dmi'
|
||||
icon_state = "asteroid"
|
||||
icon_plating = "asteroid"
|
||||
postdig_icon_change = TRUE
|
||||
var/environment_type = "asteroid"
|
||||
var/turf_type = /turf/open/floor/plating/asteroid //Because caves do whacky shit to revert to normal
|
||||
var/dug = 0 //0 = has not yet been dug, 1 = has already been dug
|
||||
var/sand_type = /obj/item/ore/glass
|
||||
var/floor_variance = 20 //probability floor has a different icon state
|
||||
archdrops = list(/obj/item/ore/glass = 5)
|
||||
|
||||
/turf/open/floor/plating/asteroid/Initialize()
|
||||
var/proper_name = name
|
||||
@@ -22,6 +22,9 @@
|
||||
if(prob(floor_variance))
|
||||
icon_state = "[environment_type][rand(0,12)]"
|
||||
|
||||
if(LAZYLEN(archdrops))
|
||||
AddComponent(/datum/component/archaeology, 100, archdrops)
|
||||
|
||||
/turf/open/floor/plating/asteroid/burn_tile()
|
||||
return
|
||||
|
||||
@@ -31,46 +34,7 @@
|
||||
/turf/open/floor/plating/asteroid/MakeDry(wet_setting = TURF_WET_WATER)
|
||||
return
|
||||
|
||||
/turf/open/floor/plating/asteroid/ex_act(severity, target)
|
||||
contents_explosion(severity, target)
|
||||
switch(severity)
|
||||
if(3)
|
||||
return
|
||||
if(2)
|
||||
if(prob(20))
|
||||
src.gets_dug()
|
||||
if(1)
|
||||
src.gets_dug()
|
||||
|
||||
/turf/open/floor/plating/asteroid/attackby(obj/item/W, mob/user, params)
|
||||
//note that this proc does not call ..()
|
||||
if(!W || !user)
|
||||
return 0
|
||||
var/digging_speed = 0
|
||||
if (istype(W, /obj/item/shovel))
|
||||
var/obj/item/shovel/S = W
|
||||
digging_speed = S.digspeed
|
||||
else if (istype(W, /obj/item/pickaxe))
|
||||
var/obj/item/pickaxe/P = W
|
||||
digging_speed = P.digspeed
|
||||
if (digging_speed)
|
||||
var/turf/T = user.loc
|
||||
if(!isturf(T))
|
||||
return
|
||||
|
||||
if (dug)
|
||||
to_chat(user, "<span class='warning'>This area has already been dug!</span>")
|
||||
return
|
||||
|
||||
to_chat(user, "<span class='notice'>You start digging...</span>")
|
||||
playsound(src, 'sound/effects/shovel_dig.ogg', 50, 1)
|
||||
|
||||
if(do_after(user, digging_speed, target = src))
|
||||
if(istype(src, /turf/open/floor/plating/asteroid))
|
||||
to_chat(user, "<span class='notice'>You dig a hole.</span>")
|
||||
gets_dug()
|
||||
SSblackbox.add_details("pick_used_mining","[W.type]")
|
||||
|
||||
if(istype(W, /obj/item/storage/bag/ore))
|
||||
var/obj/item/storage/bag/ore/S = W
|
||||
if(S.collection_mode == 1)
|
||||
@@ -88,37 +52,16 @@
|
||||
var/turf/open/floor/light/F = T
|
||||
F.state = L.state
|
||||
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
|
||||
/turf/open/floor/plating/asteroid/proc/gets_dug()
|
||||
if(dug)
|
||||
return
|
||||
for(var/i in 1 to 5)
|
||||
new sand_type(src)
|
||||
dug = 1
|
||||
icon_plating = "[environment_type]_dug"
|
||||
icon_state = "[environment_type]_dug"
|
||||
slowdown = 0
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/turf/open/floor/plating/asteroid/singularity_act()
|
||||
if(turf_z_is_planet(src))
|
||||
return ..()
|
||||
ChangeTurf(/turf/open/space)
|
||||
|
||||
/turf/open/floor/plating/asteroid/singularity_pull(S, current_size)
|
||||
if(dug)
|
||||
return
|
||||
switch(current_size)
|
||||
if(STAGE_THREE)
|
||||
if(!prob(30))
|
||||
gets_dug()
|
||||
if(STAGE_FOUR)
|
||||
if(prob(50))
|
||||
gets_dug()
|
||||
else
|
||||
if(current_size >= STAGE_FIVE && prob(70))
|
||||
gets_dug()
|
||||
|
||||
|
||||
/turf/open/floor/plating/asteroid/basalt
|
||||
name = "volcanic floor"
|
||||
@@ -127,7 +70,7 @@
|
||||
icon_state = "basalt"
|
||||
icon_plating = "basalt"
|
||||
environment_type = "basalt"
|
||||
sand_type = /obj/item/ore/glass/basalt
|
||||
archdrops = list(/obj/item/ore/glass/basalt = 5)
|
||||
floor_variance = 15
|
||||
|
||||
/turf/open/floor/plating/asteroid/basalt/lava //lava underneath
|
||||
@@ -147,10 +90,10 @@
|
||||
if("basalt5", "basalt9")
|
||||
B.set_light(1.4, 0.6, LIGHT_COLOR_LAVA) //barely anything!
|
||||
|
||||
/turf/open/floor/plating/asteroid/basalt/gets_dug()
|
||||
if(!dug)
|
||||
set_light(0)
|
||||
/turf/open/floor/plating/asteroid/basalt/ComponentActivated(datum/component/C)
|
||||
..()
|
||||
if(istype(C, /datum/component/archaeology))
|
||||
set_light(0)
|
||||
|
||||
|
||||
///////Surface. The surface is warm, but survivable without a suit. Internals are required. The floors break to chasms, which drop you into the underground.
|
||||
@@ -340,8 +283,8 @@
|
||||
initial_gas_mix = "TEMP=180"
|
||||
slowdown = 2
|
||||
environment_type = "snow"
|
||||
sand_type = /obj/item/stack/sheet/mineral/snow
|
||||
flags_1 = NONE
|
||||
archdrops = list(/obj/item/stack/sheet/mineral/snow = 5)
|
||||
|
||||
/turf/open/floor/plating/asteroid/snow/airless
|
||||
initial_gas_mix = "TEMP=2.7"
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
make_plating(1)
|
||||
|
||||
/turf/open/floor/engine/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
if(floor_tile)
|
||||
if(prob(30))
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
icon_state = "r_wall"
|
||||
|
||||
/turf/closed/wall/r_wall/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
if(prob(30))
|
||||
dismantle_wall()
|
||||
|
||||
@@ -245,6 +245,7 @@
|
||||
QDEL_IN(O, 50)
|
||||
|
||||
/turf/closed/wall/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
if(prob(50))
|
||||
dismantle_wall()
|
||||
|
||||
@@ -524,12 +524,12 @@
|
||||
|
||||
/turf/proc/photograph(limit=20)
|
||||
var/image/I = new()
|
||||
I.overlays += src
|
||||
I.add_overlay(src)
|
||||
for(var/V in contents)
|
||||
var/atom/A = V
|
||||
if(A.invisibility)
|
||||
continue
|
||||
I.overlays += A
|
||||
I.add_overlay(A)
|
||||
if(limit)
|
||||
limit--
|
||||
else
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
GLOBAL_VAR(security_mode)
|
||||
GLOBAL_PROTECT(security_mode)
|
||||
|
||||
/world/New()
|
||||
log_world("World loaded at [time_stamp()]")
|
||||
|
||||
@@ -5,6 +8,8 @@
|
||||
|
||||
GLOB.config_error_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = file("data/logs/config_error.log") //temporary file used to record errors with loading config, moved to log directory once logging is set bl
|
||||
|
||||
CheckSecurityMode()
|
||||
|
||||
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
|
||||
|
||||
config = new
|
||||
@@ -94,6 +99,20 @@
|
||||
if(GLOB.round_id)
|
||||
log_game("Round ID: [GLOB.round_id]")
|
||||
|
||||
/world/proc/CheckSecurityMode()
|
||||
//try to write to data
|
||||
if(!text2file("The world is running at least safe mode", "data/server_security_check.lock"))
|
||||
GLOB.security_mode = SECURITY_ULTRASAFE
|
||||
warning("/tg/station 13 is not supported in ultrasafe security mode. Everything will break!")
|
||||
return
|
||||
|
||||
//try to shell
|
||||
if(shell("echo \"The world is running in trusted mode\"") != null)
|
||||
GLOB.security_mode = SECURITY_TRUSTED
|
||||
else
|
||||
GLOB.security_mode = SECURITY_SAFE
|
||||
warning("/tg/station 13 uses many file operations, a few shell()s, and some external call()s. Trusted mode is recommended. You can download our source code for your own browsing and compilation at https://github.com/tgstation/tgstation")
|
||||
|
||||
/world/Topic(T, addr, master, key)
|
||||
var/list/input = params2list(T)
|
||||
|
||||
|
||||
@@ -261,6 +261,8 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
verbs += GLOB.admin_verbs_poll
|
||||
if(rights & R_SOUNDS)
|
||||
verbs += GLOB.admin_verbs_sounds
|
||||
if(config.invoke_youtubedl)
|
||||
verbs += /client/proc/play_web_sound
|
||||
if(rights & R_SPAWN)
|
||||
verbs += GLOB.admin_verbs_spawn
|
||||
|
||||
@@ -283,6 +285,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/client/proc/stealth,
|
||||
GLOB.admin_verbs_poll,
|
||||
GLOB.admin_verbs_sounds,
|
||||
/client/proc/play_web_sound,
|
||||
GLOB.admin_verbs_spawn,
|
||||
/*Debug verbs added by "show debug verbs"*/
|
||||
/client/proc/Cell,
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
return 0
|
||||
|
||||
/proc/jobban_buildcache(client/C)
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
if(C && istype(C))
|
||||
C.jobbancache = list()
|
||||
var/datum/DBQuery/query_jobban_build_cache = SSdbcore.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
|
||||
|
||||
@@ -36,7 +36,11 @@
|
||||
|
||||
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)
|
||||
SEND_SOUND(M, admin_sound)
|
||||
admin_sound.volume = vol
|
||||
|
||||
SSblackbox.add_details("admin_verb","Play Global Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -52,6 +56,62 @@
|
||||
playsound(get_turf(src.mob), S, 50, 0, 0)
|
||||
SSblackbox.add_details("admin_verb","Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_web_sound()
|
||||
set category = "Fun"
|
||||
set name = "Play Internet Sound"
|
||||
if(!check_rights(R_SOUNDS))
|
||||
return
|
||||
|
||||
if(!config.invoke_youtubedl)
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl was not configured, action unavailable</span>") //Check config.txt for the INVOKE_YOUTUBEDL value
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound via youtube-dl") as text|null
|
||||
if(istext(web_sound_input))
|
||||
var/web_sound_url = ""
|
||||
var/pitch
|
||||
if(length(web_sound_input))
|
||||
|
||||
web_sound_input = trim(web_sound_input)
|
||||
var/static/regex/html_protocol_regex = regex("https?://")
|
||||
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, html_protocol_regex))
|
||||
to_chat(src, "<span class='boldwarning'>Non-http(s) URIs are not allowed.</span>")
|
||||
to_chat(src, "<span class='warning'>For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website.</span>")
|
||||
return
|
||||
var/shell_scrubbed_input = shell_url_scrub(web_sound_input)
|
||||
var/list/output = world.shelleo("[config.invoke_youtubedl] --format \"bestaudio\[ext=aac]/bestaudio\[ext=mp3]/bestaudio\[ext=m4a]\" --get-url \"[shell_scrubbed_input]\"")
|
||||
var/errorlevel = output[SHELLEO_ERRORLEVEL]
|
||||
var/stdout = output[SHELLEO_STDOUT]
|
||||
var/stderr = output[SHELLEO_STDERR]
|
||||
if(!errorlevel)
|
||||
var/static/regex/content_url_regex = regex("https?://\\S+")
|
||||
if(content_url_regex.Find(stdout))
|
||||
web_sound_url = content_url_regex.match
|
||||
|
||||
if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
|
||||
pitch = pick(0.5, 0.7, 0.8, 0.85, 0.9, 0.95, 1.1, 1.2, 1.4, 1.6, 2.0, 2.5)
|
||||
to_chat(src, "You feel the Honkmother messing with your song...")
|
||||
|
||||
log_admin("[key_name(src)] played web sound: [web_sound_input]")
|
||||
message_admins("[key_name(src)] played web sound: [web_sound_input]")
|
||||
else
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl URL retrieval FAILED:</span>")
|
||||
to_chat(src, "<span class='warning'>[stderr]</span>")
|
||||
|
||||
else //pressed ok with blank
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
message_admins("[key_name(src)] stopped web sound")
|
||||
web_sound_url = " "
|
||||
|
||||
if(web_sound_url)
|
||||
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_url, pitch)
|
||||
|
||||
SSblackbox.add_details("admin_verb","Play Internet Sound")
|
||||
|
||||
/client/proc/set_round_end_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Set Round End Sound"
|
||||
@@ -75,4 +135,7 @@
|
||||
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.sendMusic(" ")
|
||||
SSblackbox.add_details("admin_verb","Stop All Playing Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -222,6 +222,7 @@ Pipelines + Other Objects -> Pipe network
|
||||
build_network()
|
||||
|
||||
/obj/machinery/atmospherics/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct(FALSE)
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
return 1
|
||||
|
||||
/obj/machinery/meter/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
new /obj/item/pipe_meter(loc)
|
||||
qdel(src)
|
||||
|
||||
@@ -144,6 +144,9 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, togglemidis)()
|
||||
else
|
||||
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.sendMusic(" ")
|
||||
SSblackbox.add_details("preferences_verb","Toggle Hearing Midis|[usr.client.prefs.toggles & SOUND_MIDI]") //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
|
||||
@@ -230,6 +233,9 @@ TOGGLE_CHECKBOX(/datum/verbs/menu/Settings/Sound, toggleprayersounds)()
|
||||
set category = "Preferences"
|
||||
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.sendMusic(" ")
|
||||
SSblackbox.add_details("preferences_verb","Stop Self Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
|
||||
@@ -142,69 +142,55 @@
|
||||
user.set_machine(src)
|
||||
interact(user)
|
||||
|
||||
/*******************
|
||||
* SmartFridge Menu
|
||||
********************/
|
||||
|
||||
/obj/machinery/smartfridge/interact(mob/user)
|
||||
if(stat)
|
||||
return FALSE
|
||||
|
||||
var/dat = "<TT><b>Select an item:</b><br>"
|
||||
/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "smartvend", name, 440, 550, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
if (contents.len == 0)
|
||||
dat += "<font color = 'red'>No product loaded!</font>"
|
||||
else
|
||||
var/listofitems = list()
|
||||
for (var/atom/movable/O in contents)
|
||||
if (listofitems[O.name])
|
||||
listofitems[O.name]++
|
||||
else
|
||||
listofitems[O.name] = 1
|
||||
sortList(listofitems)
|
||||
/obj/machinery/smartfridge/ui_data(mob/user)
|
||||
. = list()
|
||||
|
||||
for (var/O in listofitems)
|
||||
if(listofitems[O] <= 0)
|
||||
continue
|
||||
var/N = listofitems[O]
|
||||
var/itemName = url_encode(O)
|
||||
dat += "<FONT color = 'blue'><B>[capitalize(O)]</B>:"
|
||||
dat += " [N] </font>"
|
||||
dat += "<a href='byond://?src=\ref[src];vend=[itemName];amount=1'>Vend</A> "
|
||||
if(N > 5)
|
||||
dat += "(<a href='byond://?src=\ref[src];vend=[itemName];amount=5'>x5</A>)"
|
||||
if(N > 10)
|
||||
dat += "(<a href='byond://?src=\ref[src];vend=[itemName];amount=10'>x10</A>)"
|
||||
if(N > 25)
|
||||
dat += "(<a href='byond://?src=\ref[src];vend=[itemName];amount=25'>x25</A>)"
|
||||
if(N > 1)
|
||||
dat += "(<a href='?src=\ref[src];vend=[itemName];amount=[N]'>All</A>)"
|
||||
var/listofitems = list()
|
||||
for (var/I in src)
|
||||
var/atom/movable/O = I
|
||||
if (listofitems[O.name])
|
||||
listofitems[O.name]["amount"]++
|
||||
else
|
||||
listofitems[O.name] = list("name" = O.name, "type" = O.type, "amount" = 1)
|
||||
sortList(listofitems)
|
||||
|
||||
dat += "<br>"
|
||||
.["contents"] = listofitems
|
||||
.["name"] = name
|
||||
.["isdryer"] = FALSE
|
||||
|
||||
dat += "</TT>"
|
||||
user << browse("<HEAD><TITLE>[src] supplies</TITLE></HEAD><TT>[dat]</TT>", "window=smartfridge")
|
||||
onclose(user, "smartfridge")
|
||||
return dat
|
||||
|
||||
/obj/machinery/smartfridge/Topic(var/href, var/list/href_list)
|
||||
if(..())
|
||||
/obj/machinery/smartfridge/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
usr.set_machine(src)
|
||||
switch(action)
|
||||
if("Release")
|
||||
var/desired = 0
|
||||
|
||||
var/N = href_list["vend"]
|
||||
var/amount = text2num(href_list["amount"])
|
||||
if (params["amount"])
|
||||
desired = text2num(params["amount"])
|
||||
else
|
||||
desired = input("How many items?", "How many items would you like to take out?", 1) as null|num
|
||||
|
||||
var/i = amount
|
||||
for(var/obj/O in contents)
|
||||
if(i <= 0)
|
||||
break
|
||||
if(O.name == N)
|
||||
O.loc = src.loc
|
||||
i--
|
||||
if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src)) // Sanity checkin' in case stupid stuff happens while we wait for input()
|
||||
return FALSE
|
||||
|
||||
|
||||
updateUsrDialog()
|
||||
for(var/obj/item/O in src)
|
||||
if(desired <= 0)
|
||||
break
|
||||
if(O.name == params["name"])
|
||||
O.forceMove(drop_location())
|
||||
desired--
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
// ----------------------------
|
||||
@@ -240,20 +226,35 @@
|
||||
/obj/machinery/smartfridge/drying_rack/default_deconstruction_crowbar(obj/item/crowbar/C, ignore_panel = 1)
|
||||
..()
|
||||
|
||||
/obj/machinery/smartfridge/drying_rack/interact(mob/user)
|
||||
var/dat = ..()
|
||||
if(dat)
|
||||
dat += "<br>"
|
||||
dat += "<a href='byond://?src=\ref[src];dry=1'>Toggle Drying</A> "
|
||||
user << browse("<HEAD><TITLE>[src] supplies</TITLE></HEAD><TT>[dat]</TT>", "window=smartfridge")
|
||||
onclose(user, "smartfridge")
|
||||
/obj/machinery/smartfridge/drying_rack/ui_data(mob/user)
|
||||
. = list()
|
||||
|
||||
/obj/machinery/smartfridge/drying_rack/Topic(href, list/href_list)
|
||||
..()
|
||||
if(href_list["dry"])
|
||||
toggle_drying(FALSE)
|
||||
updateUsrDialog()
|
||||
update_icon()
|
||||
var/listofitems = list()
|
||||
for (var/I in src)
|
||||
var/atom/movable/O = I
|
||||
|
||||
if (listofitems[O.name])
|
||||
listofitems[O.name]["amount"]++
|
||||
else
|
||||
listofitems[O.name] = list("name" = O.name, "type" = O.type, "amount" = 1)
|
||||
sortList(listofitems)
|
||||
|
||||
.["contents"] = listofitems
|
||||
.["name"] = name
|
||||
.["isdryer"] = TRUE
|
||||
.["verb"] = "Take"
|
||||
.["drying"] = drying
|
||||
|
||||
|
||||
/obj/machinery/smartfridge/drying_rack/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("Dry")
|
||||
toggle_drying(FALSE)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/smartfridge/drying_rack/power_change()
|
||||
if(powered() && anchored)
|
||||
|
||||
@@ -13,6 +13,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
|
||||
var/cookieSent = FALSE // Has the client sent a cookie for analysis
|
||||
var/broken = FALSE
|
||||
var/list/connectionHistory //Contains the connection history passed from chat cookie
|
||||
var/adminMusicVolume = 100 //This is for the Play Global Sound verb
|
||||
|
||||
/datum/chatOutput/New(client/C)
|
||||
owner = C
|
||||
@@ -79,6 +80,9 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
|
||||
if("analyzeClientData")
|
||||
data = analyzeClientData(arglist(params))
|
||||
|
||||
if("setMusicVolume")
|
||||
data = setMusicVolume(arglist(params))
|
||||
|
||||
if(data)
|
||||
ehjax_send(data = data)
|
||||
|
||||
@@ -120,6 +124,16 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
|
||||
data = json_encode(data)
|
||||
C << output("[data]", "[window]:ehjaxCallback")
|
||||
|
||||
/datum/chatOutput/proc/sendMusic(music, pitch)
|
||||
var/list/music_data = list("adminMusic" = url_encode(url_encode(music)))
|
||||
if(pitch)
|
||||
music_data["musicRate"] = pitch
|
||||
ehjax_send(data = music_data)
|
||||
|
||||
/datum/chatOutput/proc/setMusicVolume(volume = "")
|
||||
if(volume)
|
||||
adminMusicVolume = Clamp(text2num(volume), 0, 100)
|
||||
|
||||
//Sends client connection details to the chat to handle and save
|
||||
/datum/chatOutput/proc/sendClientData()
|
||||
//Get dem deets
|
||||
|
||||
@@ -101,7 +101,7 @@ a.popt {text-decoration: none;}
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
#options a {
|
||||
#options .optionsCell {
|
||||
background: #ddd;
|
||||
height: 30px;
|
||||
padding: 5px 0;
|
||||
@@ -111,7 +111,7 @@ a.popt {text-decoration: none;}
|
||||
line-height: 28px;
|
||||
border-top: 1px solid #b4b4b4;
|
||||
}
|
||||
#options a:hover {background: #ccc;}
|
||||
#options .optionsCell:hover {background: #ccc;}
|
||||
#options .toggle {
|
||||
width: 40px;
|
||||
background: #ccc;
|
||||
@@ -121,7 +121,7 @@ a.popt {text-decoration: none;}
|
||||
}
|
||||
#options .sub {clear: both; display: none; width: 160px;}
|
||||
#options .sub.scroll {overflow-y: scroll;}
|
||||
#options .sub a {padding: 3px 0 3px 8px; line-height: 30px; font-size: 0.9em; clear: both;}
|
||||
#options .sub.optionsCell {padding: 3px 0 3px 8px; line-height: 30px; font-size: 0.9em; clear: both;}
|
||||
#options .sub span {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
@@ -136,6 +136,13 @@ a.popt {text-decoration: none;}
|
||||
line-height: 30px;
|
||||
float: right;
|
||||
}
|
||||
#options .sub input {
|
||||
position: absolute;
|
||||
padding: 7px 5px;
|
||||
width: 121px;
|
||||
line-height: 30px;
|
||||
float: left;
|
||||
}
|
||||
#options .decreaseFont {border-top: 0;}
|
||||
|
||||
/* POPUPS */
|
||||
|
||||
@@ -28,17 +28,19 @@
|
||||
<span class="ms" id="pingMs">--ms</span>
|
||||
</div>
|
||||
<div id="options">
|
||||
<a href="#" class="toggle" id="toggleOptions" title="Options"><i class="icon-cog"></i></a>
|
||||
<a href="#" class="optionsCell toggle" id="toggleOptions" title="Options"><i class="icon-cog"></i></a>
|
||||
<div class="sub" id="subOptions">
|
||||
<a href="#" class="decreaseFont" id="decreaseFont"><span>Decrease font size</span> <i class="icon-font">-</i></a>
|
||||
<a href="#" class="increaseFont" id="increaseFont"><span>Increase font size</span> <i class="icon-font">+</i></a>
|
||||
<a href="#" class="togglePing" id="togglePing"><span>Toggle ping display</span> <i class="icon-circle"></i></a>
|
||||
<a href="#" class="highlightTerm" id="highlightTerm"><span>Highlight string</span> <i class="icon-tag"></i></a>
|
||||
<a href="#" class="saveLog" id="saveLog"><span>Save chat log</span> <i class="icon-save"></i></a>
|
||||
<a href="#" class="clearMessages" id="clearMessages"><span>Clear all messages</span> <i class="icon-eraser"></i></a>
|
||||
<a href="#" class="optionsCell decreaseFont" id="decreaseFont"><span>Decrease font size</span> <i class="icon-font">-</i></a>
|
||||
<a href="#" class="optionsCell increaseFont" id="increaseFont"><span>Increase font size</span> <i class="icon-font">+</i></a>
|
||||
<a href="#" class="optionsCell togglePing" id="togglePing"><span>Toggle ping display</span> <i class="icon-circle"></i></a>
|
||||
<a href="#" class="optionsCell highlightTerm" id="highlightTerm"><span>Highlight string</span> <i class="icon-tag"></i></a>
|
||||
<a href="#" class="optionsCell saveLog" id="saveLog"><span>Save chat log</span> <i class="icon-save"></i></a>
|
||||
<a href="#" class="optionsCell clearMessages" id="clearMessages"><span>Clear all messages</span> <i class="icon-eraser"></i></a>
|
||||
<span class="optionsCell" id="musicVolumeSpan"><input type="range" class="hidden" id="musicVolume"><span id="musicVolumeText">Admin music volume</span><i class="icon-music"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<audio class="hidden" id="adminMusic" autoplay></audio>
|
||||
<script type="text/javascript" src="browserOutput.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -61,8 +61,17 @@ var opts = {
|
||||
'clientDataLimit': 5,
|
||||
'clientData': [],
|
||||
|
||||
//Admin music volume update
|
||||
'volumeUpdateDelay': 5000, //Time from when the volume updates to data being sent to the server
|
||||
'volumeUpdating': false, //True if volume update function set to fire
|
||||
'updatedVolume': 0, //The volume level that is sent to the server
|
||||
|
||||
};
|
||||
|
||||
function clamp(val, min, max) {
|
||||
return Math.max(min, Math.min(val, max))
|
||||
}
|
||||
|
||||
function outerHTML(el) {
|
||||
var wrap = document.createElement('div');
|
||||
wrap.appendChild(el.cloneNode(true));
|
||||
@@ -95,6 +104,15 @@ function linkify(text) {
|
||||
});
|
||||
}
|
||||
|
||||
function byondDecode(message) {
|
||||
// Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8.
|
||||
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
|
||||
// Marvelous.
|
||||
message = message.replace(/\+/g, "%20");
|
||||
message = decoder(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
//Actually turns the highlight term match into appropriate html
|
||||
function addHighlightMarkup(match) {
|
||||
var extra = '';
|
||||
@@ -176,11 +194,7 @@ function output(message, flag) {
|
||||
if (flag !== 'internal')
|
||||
opts.lastPang = Date.now();
|
||||
|
||||
// Basically we url_encode twice server side so we can manually read the encoded version and actually do UTF-8.
|
||||
// The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b.
|
||||
// Marvelous.
|
||||
message = message.replace(/\+/g, "%20")
|
||||
message = decoder(message)
|
||||
message = byondDecode(message)
|
||||
|
||||
//The behemoth of filter-code (for Admin message filters)
|
||||
//Note: This is proooobably hella inefficient
|
||||
@@ -423,7 +437,22 @@ function ehjaxCallback(data) {
|
||||
var firebugEl = document.createElement('script');
|
||||
firebugEl.src = 'https://getfirebug.com/firebug-lite-debug.js';
|
||||
document.body.appendChild(firebugEl);
|
||||
}
|
||||
} else if (data.adminMusic) {
|
||||
if (typeof data.adminMusic === 'string') {
|
||||
var adminMusic = byondDecode(data.adminMusic);
|
||||
adminMusic = adminMusic.match(/https?:\/\/\S+/) || '';
|
||||
if (data.musicRate) {
|
||||
var newRate = Number(data.musicRate);
|
||||
if(newRate) {
|
||||
$('#adminMusic').prop('defaultPlaybackRate', newRate);
|
||||
}
|
||||
} else {
|
||||
$('#adminMusic').prop('defaultPlaybackRate', 1.0);
|
||||
}
|
||||
$('#adminMusic').prop('src', adminMusic);
|
||||
$('#adminMusic').trigger("play");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,6 +475,13 @@ function toggleWasd(state) {
|
||||
opts.wasd = (state == 'on' ? true : false);
|
||||
}
|
||||
|
||||
function sendVolumeUpdate() {
|
||||
opts.volumeUpdating = false;
|
||||
if(opts.updatedVolume) {
|
||||
runByond('?_src_=chat&proc=setMusicVolume¶m[volume]='+opts.updatedVolume);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************
|
||||
*
|
||||
* DOM READY
|
||||
@@ -486,6 +522,7 @@ $(function() {
|
||||
'spingDisabled': getCookie('pingdisabled'),
|
||||
'shighlightTerms': getCookie('highlightterms'),
|
||||
'shighlightColor': getCookie('highlightcolor'),
|
||||
'smusicVolume': getCookie('musicVolume'),
|
||||
};
|
||||
|
||||
if (savedConfig.sfontSize) {
|
||||
@@ -517,6 +554,14 @@ $(function() {
|
||||
opts.highlightColor = savedConfig.shighlightColor;
|
||||
internalOutput('<span class="internal boldnshit">Loaded highlight color of: '+savedConfig.shighlightColor+'</span>', 'internal');
|
||||
}
|
||||
if (savedConfig.smusicVolume) {
|
||||
var newVolume = clamp(savedConfig.smusicVolume, 0, 100);
|
||||
$('#adminMusic').prop('volume', newVolume / 100);
|
||||
$('#musicVolume').val(newVolume);
|
||||
opts.updatedVolume = newVolume;
|
||||
sendVolumeUpdate();
|
||||
internalOutput('<span class="internal boldnshit">Loaded music volume of: '+savedConfig.smusicVolume+'</span>', 'internal');
|
||||
}
|
||||
|
||||
(function() {
|
||||
var dataCookie = getCookie('connData');
|
||||
@@ -835,6 +880,26 @@ $(function() {
|
||||
opts.messageCount = 0;
|
||||
});
|
||||
|
||||
$('#musicVolumeSpan').hover(function() {
|
||||
$('#musicVolumeText').addClass('hidden');
|
||||
$('#musicVolume').removeClass('hidden');
|
||||
}, function() {
|
||||
$('#musicVolume').addClass('hidden');
|
||||
$('#musicVolumeText').removeClass('hidden');
|
||||
});
|
||||
|
||||
$('#musicVolume').change(function() {
|
||||
var newVolume = $('#musicVolume').val();
|
||||
newVolume = clamp(newVolume, 0, 100);
|
||||
$('#adminMusic').prop('volume', newVolume / 100);
|
||||
setCookie('musicVolume', newVolume, 365);
|
||||
opts.updatedVolume = newVolume;
|
||||
if(!opts.volumeUpdating) {
|
||||
setTimeout(sendVolumeUpdate, opts.volumeUpdateDelay);
|
||||
opts.volumeUpdating = true;
|
||||
}
|
||||
});
|
||||
|
||||
$('img.icon').error(iconError);
|
||||
|
||||
|
||||
|
||||
@@ -120,17 +120,15 @@
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/structure/holohoop/CanPass(atom/movable/mover, turf/target)
|
||||
if (isitem(mover) && mover.throwing)
|
||||
var/obj/item/I = mover
|
||||
if(istype(I, /obj/item/projectile))
|
||||
return
|
||||
/obj/structure/holohoop/hitby(atom/movable/AM)
|
||||
if (isitem(AM) && !istype(AM,/obj/item/projectile))
|
||||
if(prob(50))
|
||||
I.forceMove(get_turf(src))
|
||||
visible_message("<span class='warning'>Swish! [I] lands in [src].</span>")
|
||||
AM.forceMove(get_turf(src))
|
||||
visible_message("<span class='warning'>Swish! [AM] lands in [src].</span>")
|
||||
return
|
||||
else
|
||||
visible_message("<span class='danger'>[I] bounces off of [src]'s rim!</span>")
|
||||
return 0
|
||||
visible_message("<span class='danger'>[AM] bounces off of [src]'s rim!</span>")
|
||||
return ..()
|
||||
else
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
if(istype(src, /obj/machinery/hydroponics/soil))
|
||||
add_atom_colour(rgb(255, 175, 0), FIXED_COLOUR_PRIORITY)
|
||||
else
|
||||
overlays += mutable_appearance('icons/obj/hydroponics/equipment.dmi', "gaia_blessing")
|
||||
add_overlay(mutable_appearance('icons/obj/hydroponics/equipment.dmi', "gaia_blessing"))
|
||||
set_light(3)
|
||||
|
||||
update_icon_hoses()
|
||||
|
||||
@@ -643,6 +643,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
update_hair()
|
||||
|
||||
/mob/living/carbon/human/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_THREE)
|
||||
for(var/obj/item/hand in held_items)
|
||||
if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && dropItemToGround(hand))
|
||||
@@ -651,7 +652,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
rad_act(current_size * 3)
|
||||
if(mob_negates_gravity())
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/C)
|
||||
CHECK_DNA_AND_SPECIES(C)
|
||||
|
||||
@@ -690,6 +690,7 @@
|
||||
who.equip_to_slot(what, where, TRUE)
|
||||
|
||||
/mob/living/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_SIX)
|
||||
throw_at(S,14,3, spin=1)
|
||||
else
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
/mob/living/simple_animal/drone/verb/toggle_light()
|
||||
set category = "Drone"
|
||||
set name = "Toggle drone light"
|
||||
|
||||
if(stat == DEAD)
|
||||
to_chat(src, "<span class='warning'>There's no light in your life... by that I mean you're dead.</span>")
|
||||
return
|
||||
if(light_on)
|
||||
set_light(0)
|
||||
else
|
||||
|
||||
@@ -24,11 +24,17 @@
|
||||
butcher_results = list()
|
||||
gold_core_spawnable = 2
|
||||
|
||||
/mob/living/simple_animal/pet/penguin/emperor/shamebrero
|
||||
name = "Shamebrero penguin."
|
||||
desc = "Shameful of all he surveys."
|
||||
icon_state = "penguin_shamebrero"
|
||||
icon_living = "penguin_shamebrero"
|
||||
|
||||
/mob/living/simple_animal/pet/penguin/baby
|
||||
speak = list("gah", "noot noot", "noot!", "noot", "squeee!", "noo!")
|
||||
name = "Penguin chick"
|
||||
real_name = "penguin"
|
||||
desc = "Can't fly and can barely waddles, but the prince of all chicks."
|
||||
desc = "Can't fly and barely waddles, yet the prince of all chicks."
|
||||
icon_state = "penguin_baby"
|
||||
icon_living = "penguin_baby"
|
||||
icon_dead = "penguin_baby_dead"
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
var/ranged_message = "fires" //Fluff text for ranged mobs
|
||||
var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time
|
||||
var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is
|
||||
var/ranged_telegraph = "prepares to fire at *TARGET*!" //A message shown when the mob prepares to fire; use *TARGET* if you want to show the target's name
|
||||
var/ranged_telegraph_sound //A sound played when the mob prepares to fire
|
||||
var/ranged_telegraph_time = 0 //In deciseconds, how long between the telegraph and ranged shot
|
||||
var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash
|
||||
var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting
|
||||
var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat.
|
||||
@@ -232,7 +235,14 @@
|
||||
var/target_distance = get_dist(targets_from,target)
|
||||
if(ranged) //We ranged? Shoot at em
|
||||
if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown
|
||||
OpenFire(target)
|
||||
if(!ranged_telegraph_time || client)
|
||||
OpenFire(target)
|
||||
else
|
||||
if(ranged_telegraph)
|
||||
visible_message("<span class='danger'>[src] [replacetext(ranged_telegraph, "*TARGET*", "[target]")]</span>")
|
||||
if(ranged_telegraph_sound)
|
||||
playsound(src, ranged_telegraph_sound, 75, FALSE)
|
||||
addtimer(CALLBACK(src, .proc/OpenFire, target), ranged_telegraph_time)
|
||||
if(!Process_Spacemove()) //Drifting
|
||||
walk(src,0)
|
||||
return 1
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
ranged = 1
|
||||
ranged_message = "stares"
|
||||
ranged_cooldown_time = 30
|
||||
ranged_telegraph = "gathers energy and stares at *TARGET*!"
|
||||
ranged_telegraph_sound = 'sound/magic/magic_missile.ogg'
|
||||
ranged_telegraph_time = 7
|
||||
throw_message = "does nothing against the hard shell of"
|
||||
vision_range = 2
|
||||
speed = 3
|
||||
@@ -70,9 +73,11 @@
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
attacktext = "impales"
|
||||
ranged_telegraph = "fixates on *TARGET* as its eye shines blue!"
|
||||
ranged_telegraph_sound = 'sound/magic/tail_swing.ogg'
|
||||
ranged_telegraph_time = 5
|
||||
a_intent = INTENT_HARM
|
||||
speak_emote = list("telepathically cries")
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
stat_attack = UNCONSCIOUS
|
||||
movement_type = FLYING
|
||||
robust_searching = 1
|
||||
|
||||
@@ -587,7 +587,7 @@
|
||||
var/turf/T = get_turf(client.eye)
|
||||
stat("Location:", COORD(T))
|
||||
stat("CPU:", "[world.cpu]")
|
||||
stat("Instances:", "[world.contents.len]")
|
||||
stat("Instances:", "[num2text(world.contents.len, 10)]")
|
||||
GLOB.stat_entry()
|
||||
config.stat_entry()
|
||||
stat(null)
|
||||
|
||||
@@ -170,6 +170,7 @@ By design, d1 is the smallest direction and d2 is the highest
|
||||
return 0
|
||||
|
||||
/obj/structure/cable/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct()
|
||||
|
||||
|
||||
@@ -1072,7 +1072,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
|
||||
glass_desc = "Aromatic beverage served piping hot. According to folk tales it can almost wake the dead."
|
||||
|
||||
/datum/reagent/consumable/ethanol/hearty_punch/on_mob_life(mob/living/M)
|
||||
if(M.stat == UNCONSCIOUS && M.health <= 0)
|
||||
if(M.health <= 0)
|
||||
M.adjustBruteLoss(-7, 0)
|
||||
M.adjustFireLoss(-7, 0)
|
||||
M.adjustToxLoss(-7, 0)
|
||||
|
||||
@@ -335,6 +335,7 @@
|
||||
|
||||
|
||||
/obj/structure/disposalpipe/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct()
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/machinery/disposal/singularity_pull(S, current_size)
|
||||
..()
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct()
|
||||
|
||||
@@ -335,20 +336,18 @@
|
||||
eject()
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/disposal/bin/CanPass(atom/movable/mover, turf/target)
|
||||
if (isitem(mover) && mover.throwing)
|
||||
var/obj/item/I = mover
|
||||
if(istype(I, /obj/item/projectile))
|
||||
return
|
||||
|
||||
/obj/machinery/disposal/bin/hitby(atom/movable/AM)
|
||||
if(isitem(AM) && AM.CanEnterDisposals())
|
||||
if(prob(75))
|
||||
I.forceMove(src)
|
||||
visible_message("<span class='notice'>[I] lands in [src].</span>")
|
||||
AM.forceMove(src)
|
||||
visible_message("<span class='notice'>[AM] lands in [src].</span>")
|
||||
update_icon()
|
||||
else
|
||||
visible_message("<span class='notice'>[I] bounces off of [src]'s rim!</span>")
|
||||
return 0
|
||||
visible_message("<span class='notice'>[AM] bounces off of [src]'s rim!</span>")
|
||||
return ..()
|
||||
else
|
||||
return ..(mover, target)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/disposal/bin/flush()
|
||||
..()
|
||||
@@ -457,12 +456,12 @@
|
||||
trunk.linked = src // link the pipe trunk to self
|
||||
|
||||
/obj/machinery/disposal/deliveryChute/place_item_in_disposal(obj/item/I, mob/user)
|
||||
if(I.disposalEnterTry())
|
||||
if(I.CanEnterDisposals())
|
||||
..()
|
||||
flush()
|
||||
|
||||
/obj/machinery/disposal/deliveryChute/CollidedWith(atom/movable/AM) //Go straight into the chute
|
||||
if(!AM.disposalEnterTry())
|
||||
if(!AM.CanEnterDisposals())
|
||||
return
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
@@ -485,16 +484,16 @@
|
||||
M.forceMove(src)
|
||||
flush()
|
||||
|
||||
/atom/movable/proc/disposalEnterTry()
|
||||
/atom/movable/proc/CanEnterDisposals()
|
||||
return 1
|
||||
|
||||
/obj/item/projectile/disposalEnterTry()
|
||||
/obj/item/projectile/CanEnterDisposals()
|
||||
return
|
||||
|
||||
/obj/effect/disposalEnterTry()
|
||||
/obj/effect/CanEnterDisposals()
|
||||
return
|
||||
|
||||
/obj/mecha/disposalEnterTry()
|
||||
/obj/mecha/CanEnterDisposals()
|
||||
return
|
||||
|
||||
/obj/machinery/disposal/deliveryChute/newHolderDestination(obj/structure/disposalholder/H)
|
||||
|
||||
@@ -430,3 +430,23 @@
|
||||
materials = list(MAT_METAL = 500, MAT_GLASS = 500)
|
||||
build_path = /obj/item/organ/liver/cybernetic/upgraded
|
||||
category = list("Medical Designs")
|
||||
|
||||
/datum/design/cybernetic_lungs
|
||||
name = "Cybernetic Lungs"
|
||||
desc = "A pair of cybernetic lungs."
|
||||
id = "cybernetic_lungs"
|
||||
req_tech = list("biotech" = 4, "materials" = 4)
|
||||
build_type = PROTOLATHE
|
||||
materials = list(MAT_METAL = 500, MAT_GLASS = 500)
|
||||
build_path = /obj/item/organ/lungs/cybernetic
|
||||
category = list("Medical Designs")
|
||||
|
||||
/datum/design/cybernetic_lungs_u
|
||||
name = "Upgraded Cybernetic Lungs"
|
||||
desc = "A pair of upgraded cybernetic lungs."
|
||||
id = "cybernetic_lungs_u"
|
||||
req_tech = list("biotech" = 5, "materials" = 5, "engineering" = 5)
|
||||
build_type = PROTOLATHE
|
||||
materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 500)
|
||||
build_path = /obj/item/organ/lungs/cybernetic/upgraded
|
||||
category = list("Medical Designs")
|
||||
@@ -46,6 +46,7 @@
|
||||
desc = "The incomplete body of a golem. Add ten sheets of any mineral to finish."
|
||||
var/shell_type = /obj/effect/mob_spawn/human/golem
|
||||
var/has_owner = FALSE //if the resulting golem obeys someone
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
|
||||
/obj/item/golem_shell/attackby(obj/item/I, mob/user, params)
|
||||
..()
|
||||
|
||||
@@ -4,6 +4,10 @@ GLOBAL_PROTECT(reboot_mode)
|
||||
/world/proc/RunningService()
|
||||
return params[SERVICE_WORLD_PARAM]
|
||||
|
||||
/proc/ServiceVersion()
|
||||
if(world.RunningService())
|
||||
return world.params[SERVICE_VERSION_PARAM]
|
||||
|
||||
/world/proc/ExportService(command)
|
||||
return RunningService() && shell("python code/modules/server_tools/nudge.py \"[command]\"") == 0
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ All ShuttleMove procs go here
|
||||
// Called from the new turf before anything has been moved
|
||||
// Only gets called if fromShuttleMove returns true first
|
||||
// returns the new move_mode (based on the old)
|
||||
/turf/proc/toShuttleMove(turf/oldT, shuttle_dir, move_mode)
|
||||
/turf/proc/toShuttleMove(turf/oldT, move_mode, obj/docking_port/mobile/shuttle)
|
||||
var/shuttle_dir = shuttle.dir
|
||||
for(var/i in contents)
|
||||
var/atom/movable/thing = i
|
||||
if(ismob(thing))
|
||||
@@ -383,4 +384,4 @@ All ShuttleMove procs go here
|
||||
|
||||
/obj/effect/abstract/proximity_checker/onShuttleMove(turf/newT, turf/oldT, rotation, list/movement_force, move_dir, old_dock)
|
||||
//timer so it only happens once
|
||||
addtimer(CALLBACK(monitor, /datum/proximity_monitor/proc/SetRange, monitor.current_range, TRUE), 0, TIMER_UNIQUE)
|
||||
addtimer(CALLBACK(monitor, /datum/proximity_monitor/proc/SetRange, monitor.current_range, TRUE), 0, TIMER_UNIQUE)
|
||||
|
||||
@@ -570,7 +570,7 @@
|
||||
move_mode = moving_atom.beforeShuttleMove(newT, rotation, move_mode) //atoms
|
||||
|
||||
move_mode = oldT.fromShuttleMove(newT, underlying_turf_type, baseturf_cache, move_mode) //turfs
|
||||
move_mode = newT.toShuttleMove(oldT, dir, move_mode) //turfs
|
||||
move_mode = newT.toShuttleMove(oldT, move_mode , src) //turfs
|
||||
|
||||
if(move_mode & MOVE_AREA)
|
||||
areas_to_move[old_area] = TRUE
|
||||
|
||||
@@ -215,23 +215,37 @@
|
||||
/obj/effect/forcefield/luxury_shuttle
|
||||
var/threshold = 500
|
||||
var/static/list/approved_passengers = list()
|
||||
var/static/list/check_times = list()
|
||||
|
||||
/obj/effect/forcefield/luxury_shuttle/CanPass(atom/movable/mover, turf/target)
|
||||
if(mover in approved_passengers)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
if(!isliving(mover)) //No stowaways
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
return FALSE
|
||||
|
||||
|
||||
#define LUXURY_MESSAGE_COOLDOWN 100
|
||||
/obj/effect/forcefield/luxury_shuttle/CollidedWith(atom/movable/AM)
|
||||
if(!isliving(AM))
|
||||
return ..()
|
||||
|
||||
if(check_times[AM] && check_times[AM] > world.time) //Let's not spam the message
|
||||
return ..()
|
||||
|
||||
check_times[AM] = world.time + LUXURY_MESSAGE_COOLDOWN
|
||||
|
||||
var/total_cash = 0
|
||||
var/list/counted_money = list()
|
||||
|
||||
for(var/obj/item/coin/C in mover.GetAllContents())
|
||||
for(var/obj/item/coin/C in AM.GetAllContents())
|
||||
total_cash += C.value
|
||||
counted_money += C
|
||||
if(total_cash >= threshold)
|
||||
break
|
||||
for(var/obj/item/stack/spacecash/S in mover.GetAllContents())
|
||||
for(var/obj/item/stack/spacecash/S in AM.GetAllContents())
|
||||
total_cash += S.value * S.amount
|
||||
counted_money += S
|
||||
if(total_cash >= threshold)
|
||||
@@ -241,12 +255,13 @@
|
||||
for(var/obj/I in counted_money)
|
||||
qdel(I)
|
||||
|
||||
to_chat(mover, "Thank you for your payment! Please enjoy your flight.")
|
||||
approved_passengers += mover
|
||||
return 1
|
||||
to_chat(AM, "Thank you for your payment! Please enjoy your flight.")
|
||||
approved_passengers += AM
|
||||
check_times -= AM
|
||||
return
|
||||
else
|
||||
to_chat(mover, "You don't have enough money to enter the main shuttle. You'll have to fly coach.")
|
||||
return 0
|
||||
to_chat(AM, "<span class='warning'>You don't have enough money to enter the main shuttle. You'll have to fly coach.</span>")
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/fightpit
|
||||
name = "fight pit bear"
|
||||
|
||||
@@ -316,6 +316,29 @@
|
||||
safe_toxins_min = 16 //We breath THIS!
|
||||
safe_toxins_max = 0
|
||||
|
||||
/obj/item/organ/lungs/cybernetic
|
||||
name = "cybernetic lungs"
|
||||
desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement."
|
||||
icon_state = "lungs-c"
|
||||
origin_tech = "biotech=4"
|
||||
|
||||
/obj/item/organ/lungs/cybernetic/emp_act()
|
||||
owner.losebreath = 20
|
||||
|
||||
|
||||
/obj/item/organ/lungs/cybernetic/upgraded
|
||||
name = "upgraded cybernetic lungs"
|
||||
desc = "A more advanced version of the stock cybernetic lungs. They are capable of filtering out lower levels of toxins and carbon-dioxide."
|
||||
icon_state = "lungs-c-u"
|
||||
origin_tech = "biotech=5"
|
||||
|
||||
safe_toxins_max = 20
|
||||
safe_co2_max = 20
|
||||
|
||||
cold_level_1_threshold = 200
|
||||
cold_level_2_threshold = 140
|
||||
cold_level_3_threshold = 100
|
||||
|
||||
#undef HUMAN_MAX_OXYLOSS
|
||||
#undef HUMAN_CRIT_MAX_OXYLOSS
|
||||
#undef HEAT_GAS_DAMAGE_LEVEL_1
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
say_mod = "hisses"
|
||||
taste_sensitivity = 10 // combined nose + tongue, extra sensitive
|
||||
|
||||
/*
|
||||
/obj/item/organ/tongue/lizard/TongueSpeech(var/message)
|
||||
var/regex/lizard_hiss = new("s+", "g")
|
||||
var/regex/lizard_hiSS = new("S+", "g")
|
||||
@@ -54,6 +55,7 @@
|
||||
message = lizard_hiss.Replace(message, "sss")
|
||||
message = lizard_hiSS.Replace(message, "SSS")
|
||||
return message
|
||||
*/
|
||||
|
||||
/obj/item/organ/tongue/fly
|
||||
name = "proboscis"
|
||||
|
||||
@@ -191,6 +191,14 @@ CHECK_RANDOMIZER
|
||||
## Ban appeals URL - usually for a forum or wherever people should go to contact your admins.
|
||||
# BANAPPEALS http://justanotherday.example.com
|
||||
|
||||
## System command that invokes youtube-dl, used by Play Internet Sound.
|
||||
## You can install youtube-dl with
|
||||
## "pip install youtube-dl" if you have pip installed
|
||||
## from https://github.com/rg3/youtube-dl/releases
|
||||
## or your package manager
|
||||
## The default value assumes youtube-dl is in your system PATH
|
||||
# INVOKE_YOUTUBEDL youtube-dl
|
||||
|
||||
## In-game features
|
||||
##Toggle for having jobs load up from the .txt
|
||||
# LOAD_JOBS_FROM_TXT
|
||||
|
||||
4
html/changelogs/AutoChangeLog-pr-2600.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2600.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Jay"
|
||||
delete-after: True
|
||||
changes:
|
||||
- tweak: "Removes the lisp lizards have when talking"
|
||||
4
html/changelogs/AutoChangeLog-pr-2633.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2633.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Xhuis"
|
||||
delete-after: True
|
||||
changes:
|
||||
- imageadd: "The hand drill and jaws of life now have sprites on the toolbelt."
|
||||
4
html/changelogs/AutoChangeLog-pr-2641.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2641.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "CitadelStationBot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Admins may now show the variables interface to players to help contributors debug their new additions"
|
||||
4
html/changelogs/AutoChangeLog-pr-2650.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2650.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Naksu"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Added TGUI interfaces to various smartfridges of different kinds, drying racks and the disk compartmentalizer"
|
||||
4
html/changelogs/AutoChangeLog-pr-2656.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2656.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "JJRcop"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Admins can now play media content from the web to players."
|
||||
4
html/changelogs/AutoChangeLog-pr-2662.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2662.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "basilman"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "penguins may now have shamebreros, noot noot"
|
||||
4
html/changelogs/AutoChangeLog-pr-2663.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2663.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Xhuis"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Watchers now have a half-second telegraph between charging and firing their freezing blasts."
|
||||
4
html/changelogs/AutoChangeLog-pr-2666.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2666.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Robustin"
|
||||
delete-after: True
|
||||
changes:
|
||||
- tweak: "Golem shells no longer fit in standard crew bags."
|
||||
4
html/changelogs/AutoChangeLog-pr-2671.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2671.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "CitadelStationBot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Windoors open when emagged again"
|
||||
4
html/changelogs/AutoChangeLog-pr-2674.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2674.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "VexingRaven"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Hearty Punch once again pulls people out of crit."
|
||||
4
html/changelogs/AutoChangeLog-pr-2688.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2688.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Firecage"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "The NanoTrasen Department for Cybernetics (NDC) would like to announce the creation of Cybernetic Lungs. Both a stock variety to replace traditional stock human lungs in emergencies, and a more enhanced variety allowing greater tolerance of breathing cold air, toxins, and CO2."
|
||||
4
html/changelogs/AutoChangeLog-pr-2706.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-2706.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Xhuis"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "You can now use metal rods and departmental jumpsuits to craft departments for each banner."
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
@@ -95,6 +95,7 @@
|
||||
#include "code\__HELPERS\pronouns.dm"
|
||||
#include "code\__HELPERS\radio.dm"
|
||||
#include "code\__HELPERS\sanitize_values.dm"
|
||||
#include "code\__HELPERS\shell.dm"
|
||||
#include "code\__HELPERS\text.dm"
|
||||
#include "code\__HELPERS\text_vr.dm"
|
||||
#include "code\__HELPERS\time.dm"
|
||||
@@ -281,6 +282,7 @@
|
||||
#include "code\datums\antagonists\datum_traitor.dm"
|
||||
#include "code\datums\antagonists\devil.dm"
|
||||
#include "code\datums\antagonists\ninja.dm"
|
||||
#include "code\datums\components\archaeology.dm"
|
||||
#include "code\datums\components\component.dm"
|
||||
#include "code\datums\components\slippery.dm"
|
||||
#include "code\datums\diseases\_disease.dm"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
48
tgui/src/interfaces/smartvend.ract
Normal file
48
tgui/src/interfaces/smartvend.ract
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
<ui-display title='Storage' button>
|
||||
{{#partial button}}
|
||||
{{#if data.isdryer}}<ui-button icon='{{data.drying ? "stop" : "tint"}}' action='Dry'>{{data.drying ? 'Stop drying' : 'Dry'}}</ui-button>{{/if}}
|
||||
{{/partial}}
|
||||
{{#if data.contents.length == 0}}
|
||||
<ui-notice>
|
||||
<span>Unfortunately, this {{data.name}} is empty.</span>
|
||||
</ui-notice>
|
||||
{{else}}
|
||||
<div class="display tabular">
|
||||
<section class="candystripe">
|
||||
<section class="cell bold">
|
||||
Item
|
||||
</section>
|
||||
<section class="cell bold">
|
||||
Quantity
|
||||
</section>
|
||||
<section class="cell bold" align='center'>
|
||||
{{#if data.verb}}{{data.verb}}{{else}}Dispense{{/if}}
|
||||
</section>
|
||||
</section>
|
||||
{{#each data.contents}}
|
||||
<section class="candystripe">
|
||||
<section class="cell">
|
||||
{{name}}
|
||||
</section>
|
||||
<section class="cell" align='right'>
|
||||
{{amount}}
|
||||
</section>
|
||||
<section class="table" alight='right'>
|
||||
<section class="cell"></section>
|
||||
<section class="cell">
|
||||
<ui-button grid action='Release' state={{(amount >= 1) ? null : 'disabled'}} params='{ "name" : {{name}}, "amount" : 1 }' >
|
||||
One
|
||||
</ui-button>
|
||||
</section>
|
||||
<section class="cell">
|
||||
<ui-button grid action='Release' state={{(amount > 1) ? null : 'disabled'}} params='{ "name" : {{name}} }' >
|
||||
Many
|
||||
</ui-button>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</ui-display>
|
||||
@@ -23,6 +23,12 @@
|
||||
//This is required as otherwise somebody could trick the script into leaking the api key.
|
||||
$hookSecret = '08ajh0qj93209qj90jfq932j32r';
|
||||
|
||||
$trackPRBalance = true; //set this to false to disable PR balance tracking
|
||||
$prBalanceJson = ''; //Set this to the path you'd like the writable pr balance file to be stored, not setting it writes it to the working directory
|
||||
$startingPRBalance = 3; //Starting balance for never before seen users
|
||||
//team 133041: tgstation/commit-access
|
||||
$maintainer_team_id = 133041; //org team id that is exempt from PR balance system, setting this to null will use anyone with write access to the repo. Get from https://api.github.com/orgs/:org/teams
|
||||
|
||||
//Api key for pushing changelogs.
|
||||
$apiKey = '209ab8d879c0f987d06a09b9d879c0f987d06a09b9d8787d0a089c';
|
||||
|
||||
@@ -168,7 +174,7 @@ function tag_pr($payload, $opened) {
|
||||
if(strpos($lowertitle, '[wip]') !== FALSE)
|
||||
$tags[] = 'Work In Progress';
|
||||
|
||||
$url = $payload['pull_request']['base']['repo']['url'] . '/issues/' . $payload['pull_request']['number'] . '/labels';
|
||||
$url = $payload['pull_request']['issue_url'] . '/labels';
|
||||
|
||||
$existing_labels = file_get_contents($url, false, stream_context_create($scontext));
|
||||
$existing_labels = json_decode($existing_labels, true);
|
||||
@@ -195,6 +201,12 @@ function handle_pr($payload) {
|
||||
switch ($payload["action"]) {
|
||||
case 'opened':
|
||||
tag_pr($payload, true);
|
||||
if(get_pr_code_friendliness($payload) < 0){
|
||||
$balances = pr_balances();
|
||||
$author = $payload['pull_request']['user']['login'];
|
||||
if(isset($balances[$author]) && $balances[$author] < 0)
|
||||
create_comment($payload, 'You currently have a negative Fix/Feature pull request delta of ' . $balances[$author] . '. Maintainers may close this PR at will. Fixing issues or improving the codebase will improve this score.');
|
||||
}
|
||||
break;
|
||||
case 'edited':
|
||||
case 'synchronize':
|
||||
@@ -210,6 +222,8 @@ function handle_pr($payload) {
|
||||
else {
|
||||
$action = 'merged';
|
||||
checkchangelog($payload, true, true);
|
||||
update_pr_balance($payload);
|
||||
$validated = TRUE; //pr merged events always get announced.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -223,7 +237,116 @@ function handle_pr($payload) {
|
||||
|
||||
$msg = '['.$payload['pull_request']['base']['repo']['full_name'].'] Pull Request '.$action.' by '.htmlSpecialChars($payload['sender']['login']).': <a href="'.$payload['pull_request']['html_url'].'">'.htmlSpecialChars('#'.$payload['pull_request']['number'].' '.$payload['pull_request']['user']['login'].' - '.$payload['pull_request']['title']).'</a>';
|
||||
sendtoallservers('?announce='.urlencode($msg), $payload);
|
||||
}
|
||||
|
||||
//creates a comment on the payload issue
|
||||
function create_comment($payload, $comment){
|
||||
apisend($payload['pull_request']['comments_url'], 'POST', json_encode(array('body' => $comment)));
|
||||
}
|
||||
|
||||
//returns the payload issue's labels as a flat array
|
||||
function get_pr_labels_array($payload){
|
||||
$url = $payload['pull_request']['issue_url'] . '/labels';
|
||||
$issue = json_decode(apisend($url), true);
|
||||
$result = array();
|
||||
foreach($issue as $l)
|
||||
$result[] = $l['name'];
|
||||
return $result;
|
||||
}
|
||||
|
||||
//helper for getting the path the the balance json file
|
||||
function pr_balance_json_path(){
|
||||
global $prBalanceJson;
|
||||
return $prBalanceJson != '' ? $prBalanceJson : 'pr_balances.json';
|
||||
}
|
||||
|
||||
//return the assoc array of login -> balance for prs
|
||||
function pr_balances(){
|
||||
$path = pr_balance_json_path();
|
||||
if(file_exists($path))
|
||||
return json_decode(file_get_contents($path), true);
|
||||
else
|
||||
return array();
|
||||
}
|
||||
|
||||
//returns the difference in PR balance a pull request would cause
|
||||
function get_pr_code_friendliness($payload, $oldbalance = null){
|
||||
global $startingPRBalance;
|
||||
if($oldbalance == null)
|
||||
$oldbalance = $startingPRBalance;
|
||||
$labels = get_pr_labels_array($payload);
|
||||
//anything not in this list defaults to 0
|
||||
$label_values = array(
|
||||
'Fix' => 2,
|
||||
'Refactor' => 2,
|
||||
'Code Improvement' => 1,
|
||||
'Priority: High' => 4,
|
||||
'Priority: CRITICAL' => 5,
|
||||
'Atmospherics' => 4,
|
||||
'Logging' => 1,
|
||||
'Feedback' => 1,
|
||||
'Performance' => 3,
|
||||
'Feature' => -1,
|
||||
'Balance/Rebalance' => -1,
|
||||
'Tweak' => -1,
|
||||
'PRB: Reset' => $startingPRBalance - $oldbalance,
|
||||
);
|
||||
|
||||
$affecting = 0;
|
||||
$is_neutral = FALSE;
|
||||
$found_something_positive = false;
|
||||
foreach($labels as $l){
|
||||
if($l == 'PRB: No Update') { //no effect on balance
|
||||
$affecting = 0;
|
||||
break;
|
||||
}
|
||||
else if(isset($label_values[$l])) {
|
||||
$friendliness = $label_values[$l];
|
||||
if($friendliness > 0)
|
||||
$found_something_positive = true;
|
||||
$affecting = $found_something_positive ? max($affecting, $friendliness) : $friendliness;
|
||||
}
|
||||
}
|
||||
return $affecting;
|
||||
}
|
||||
|
||||
function is_maintainer($payload, $author){
|
||||
global $maintainer_team_id;
|
||||
$repo_is_org = $payload['pull_request']['base']['repo']['owner']['type'] == 'Organization';
|
||||
if($maintainer_team_id == null || !$repo_is_org) {
|
||||
$collaburl = $payload['pull_request']['base']['repo']['collaborators_url'] . '/' . $author . '/permissions';
|
||||
$perms = json_decode(apisend($collaburl), true);
|
||||
$permlevel = $perms['permission'];
|
||||
return $permlevel == 'admin' || $permlevel == 'write';
|
||||
}
|
||||
else {
|
||||
$check_url = 'https://api.github.com/teams/' . $maintainer_team_id . '/memberships/' . $author;
|
||||
$result = json_decode(apisend($check_url), true);
|
||||
return isset($result['state']); //this field won't be here if they aren't a member
|
||||
}
|
||||
}
|
||||
|
||||
//payload is a merged pull request, updates the pr balances file with the correct positive or negative balance based on comments
|
||||
function update_pr_balance($payload) {
|
||||
global $startingPRBalance;
|
||||
global $trackPRBalance;
|
||||
if(!$trackPRBalance)
|
||||
return;
|
||||
$author = $payload['pull_request']['user']['login'];
|
||||
if(is_maintainer($payload, $author)) //immune
|
||||
return;
|
||||
$balances = pr_balances();
|
||||
if(!isset($balances[$author]))
|
||||
$balances[$author] = $startingPRBalance;
|
||||
$friendliness = get_pr_code_friendliness($payload, $balances[$author]);
|
||||
$balances[$author] += $friendliness;
|
||||
if($balances[$author] < 0 && $friendliness < 0)
|
||||
create_comment($payload, 'Your Fix/Feature pull request delta is currently below zero (' . $balances[$author] . '). Maintainers may close future Feature/Tweak/Balance PRs. Fixing issues or helping to improve the codebase will raise this score.');
|
||||
else if($balances[$author] >= 0 && ($balances[$author] - $friendliness) < 0)
|
||||
create_comment($payload, 'Your Fix/Feature pull request delta is now above zero (' . $balances[$author] . '). Feel free to make Feature/Tweak/Balance PRs.');
|
||||
$balances_file = fopen(pr_balance_json_path(), 'w');
|
||||
fwrite($balances_file, json_encode($balances));
|
||||
fclose($balances_file);
|
||||
}
|
||||
|
||||
function has_tree_been_edited($payload, $tree){
|
||||
|
||||
Reference in New Issue
Block a user