Merge remote-tracking branch 'upstream/master' into signaler-tgui-next

This commit is contained in:
SteelSlayer
2020-01-07 11:54:16 -06:00
113 changed files with 3408 additions and 2394 deletions
-1
View File
@@ -57,7 +57,6 @@
#define FLOODLIGHT_NEEDS_WIRES 0
#define FLOODLIGHT_NEEDS_LIGHTS 1
#define FLOODLIGHT_NEEDS_SECURING 2
#define FLOODLIGHT_NEEDS_WRENCHING 3
//other construction-related things
+1
View File
@@ -12,3 +12,4 @@
#define MATERIAL_COLOR (1<<0)
#define MATERIAL_ADD_PREFIX (1<<1)
#define MATERIAL_NO_EFFECTS (1<<2)
#define MATERIAL_AFFECT_STATISTICS (1<<3)
+6 -1
View File
@@ -1,5 +1,5 @@
// Skills
// Skill levels
#define SKILL_LEVEL_NONE 0
#define SKILL_LEVEL_NOVICE 1
#define SKILL_LEVEL_APPRENTICE 2
@@ -8,6 +8,7 @@
#define SKILL_LEVEL_MASTER 5
#define SKILL_LEVEL_LEGENDARY 6
//Skill experience thresholds
#define SKILL_EXP_NOVICE 100
#define SKILL_EXP_APPRENTICE 250
#define SKILL_EXP_JOURNEYMAN 500
@@ -15,5 +16,9 @@
#define SKILL_EXP_MASTER 1500
#define SKILL_EXP_LEGENDARY 2500
//Skill modifier types
#define SKILL_SPEED_MODIFIER "skill_speed_modifier"
// Gets the reference for the skill type that was given
#define GetSkillRef(A) (SSskills.all_skills[A])
+12 -12
View File
@@ -49,24 +49,24 @@
if(!istext(color))
color = ""
var/start = 1 + (text2ascii(color,1)==35)
var/start = 1 + (text2ascii(color, 1) == 35)
var/len = length(color)
var/step_size = 1 + ((len+1)-start != desired_format)
var/char = ""
. = ""
for(var/i=start, i<=len, i+=step_size)
var/ascii = text2ascii(color,i)
switch(ascii)
if(48 to 57)
. += ascii2text(ascii) //numbers 0 to 9
if(97 to 102)
. += ascii2text(ascii) //letters a to f
if(65 to 70)
. += ascii2text(ascii+32) //letters A to F - translates to lowercase
for(var/i = start, i <= len, i += length(char))
char = color[i]
switch(text2ascii(char))
if(48 to 57) //numbers 0 to 9
. += char
if(97 to 102) //letters a to f
. += char
if(65 to 70) //letters A to F
. += lowertext(char)
else
break
if(length(.) != desired_format)
if(length_char(.) != desired_format)
if(default)
return default
return crunch + repeat_string(desired_format, "0")
+216 -235
View File
@@ -40,8 +40,8 @@
for(var/char in repl_chars)
var/index = findtext(t, char)
while(index)
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1)
index = findtext(t, char, index+1)
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index + length(char))
index = findtext(t, char, index + length(char))
return t
/proc/sanitize_filename(t)
@@ -73,22 +73,28 @@
//Returns null if there is any bad text in the string
/proc/reject_bad_text(text, max_length=512)
if(length(text) > max_length)
return //message too long
var/non_whitespace = 0
for(var/i=1, i<=length(text), i++)
switch(text2ascii(text,i))
if(62,60,92,47)
return //rejects the text if it contains these bad characters: <, >, \ or /
if(127 to 255)
return //rejects weird letters like
/proc/reject_bad_text(text, max_length = 512, ascii_only = TRUE)
var/char_count = 0
var/non_whitespace = FALSE
var/lenbytes = length(text)
var/char = ""
for(var/i = 1, i <= lenbytes, i += length(char))
char = text[i]
char_count++
if(char_count > max_length)
return
switch(text2ascii(char))
if(62, 60, 92, 47) // <, >, \, /
return
if(0 to 31)
return //more weird stuff
return
if(32)
continue //whitespace
continue
if(127 to INFINITY)
if(ascii_only)
return
else
non_whitespace = 1
non_whitespace = TRUE
if(non_whitespace)
return text //only accepts the text if it has some non-spaces
@@ -109,73 +115,84 @@
else
return trim(html_encode(name), max_length)
#define NO_CHARS_DETECTED 0
#define SPACES_DETECTED 1
#define SYMBOLS_DETECTED 2
#define NUMBERS_DETECTED 3
#define LETTERS_DETECTED 4
//Filters out undesirable characters from names
/proc/reject_bad_name(t_in, allow_numbers=0, max_length=MAX_NAME_LEN)
if(!t_in || length(t_in) > max_length)
return //Rejects the input if it is null or if it is longer then the max length allowed
/proc/reject_bad_name(t_in, allow_numbers = FALSE, max_length = MAX_NAME_LEN, ascii_only = TRUE)
if(!t_in)
return //Rejects the input if it is null
var/number_of_alphanumeric = 0
var/last_char_group = 0
var/number_of_alphanumeric = 0
var/last_char_group = NO_CHARS_DETECTED
var/t_out = ""
var/t_len = length(t_in)
var/charcount = 0
var/char = ""
for(var/i=1, i<=length(t_in), i++)
var/ascii_char = text2ascii(t_in,i)
switch(ascii_char)
for(var/i = 1, i <= t_len, i += length(char))
char = t_in[i]
switch(text2ascii(char))
// A .. Z
if(65 to 90) //Uppercase Letters
t_out += ascii2text(ascii_char)
number_of_alphanumeric++
last_char_group = 4
last_char_group = LETTERS_DETECTED
// a .. z
if(97 to 122) //Lowercase Letters
if(last_char_group<2)
t_out += ascii2text(ascii_char-32) //Force uppercase first character
else
t_out += ascii2text(ascii_char)
if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED || last_char_group == SYMBOLS_DETECTED) //start of a word
char = uppertext(char)
number_of_alphanumeric++
last_char_group = 4
last_char_group = LETTERS_DETECTED
// 0 .. 9
if(48 to 57) //Numbers
if(!last_char_group)
continue //suppress at start of string
if(!allow_numbers)
if(last_char_group == NO_CHARS_DETECTED || !allow_numbers) //suppress at start of string
continue
t_out += ascii2text(ascii_char)
number_of_alphanumeric++
last_char_group = 3
last_char_group = NUMBERS_DETECTED
// ' - .
if(39,45,46) //Common name punctuation
if(!last_char_group)
if(last_char_group == NO_CHARS_DETECTED)
continue
t_out += ascii2text(ascii_char)
last_char_group = 2
last_char_group = SYMBOLS_DETECTED
// ~ | @ : # $ % & * +
if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI)
if(!last_char_group)
continue //suppress at start of string
if(!allow_numbers)
if(last_char_group == NO_CHARS_DETECTED || !allow_numbers) //suppress at start of string
continue
t_out += ascii2text(ascii_char)
last_char_group = 2
last_char_group = SYMBOLS_DETECTED
//Space
if(32)
if(last_char_group <= 1)
continue //suppress double-spaces and spaces at start of string
t_out += ascii2text(ascii_char)
last_char_group = 1
if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED) //suppress double-spaces and spaces at start of string
continue
last_char_group = SPACES_DETECTED
if(127 to INFINITY)
if(ascii_only)
continue
last_char_group = SYMBOLS_DETECTED //for now, we'll treat all non-ascii characters like symbols even though most are letters
else
return
continue
t_out += char
charcount++
if(charcount >= max_length)
break
if(number_of_alphanumeric < 2)
return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '"
if(last_char_group == 1)
t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space)
if(last_char_group == SPACES_DETECTED)
t_out = copytext_char(t_out, 1, -1) //removes the last character (in this case a space)
for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai")) //prevents these common metagamey names
if(cmptext(t_out,bad_name))
@@ -183,6 +200,13 @@
return t_out
#undef NO_CHARS_DETECTED
#undef SPACES_DETECTED
#undef NUMBERS_DETECTED
#undef LETTERS_DETECTED
//html_encode helper proc that returns the smallest non null of two numbers
//or 0 if they're both null (needed because of findtext returning 0 when a value is not present)
/proc/non_zero_min(a, b)
@@ -192,39 +216,6 @@
return a
return (a < b ? a : b)
/*
* Text searches
*/
//Checks the beginning of a string for a specified sub-string
//Returns the position of the substring or 0 if it was not found
/proc/dd_hasprefix(text, prefix)
var/start = 1
var/end = length(prefix) + 1
return findtext(text, prefix, start, end)
//Checks the beginning of a string for a specified sub-string. This proc is case sensitive
//Returns the position of the substring or 0 if it was not found
/proc/dd_hasprefix_case(text, prefix)
var/start = 1
var/end = length(prefix) + 1
return findtextEx(text, prefix, start, end)
//Checks the end of a string for a specified substring.
//Returns the position of the substring or 0 if it was not found
/proc/dd_hassuffix(text, suffix)
var/start = length(text) - length(suffix)
if(start)
return findtext(text, suffix, start, null)
return
//Checks the end of a string for a specified substring. This proc is case sensitive
//Returns the position of the substring or 0 if it was not found
/proc/dd_hassuffix_case(text, suffix)
var/start = length(text) - length(suffix)
if(start)
return findtextEx(text, suffix, start, null)
//Checks if any of a given list of needles is in the haystack
/proc/text_in_list(haystack, list/needle_list, start=1, end=0)
for(var/needle in needle_list)
@@ -239,23 +230,17 @@
return 1
return 0
//Adds 'u' number of zeros ahead of the text 't'
/proc/add_zero(t, u)
while (length(t) < u)
t = "0[t]"
return t
//Adds 'char' ahead of 'text' until there are 'count' characters total
/proc/add_leading(text, count, char = " ")
var/charcount = count - length_char(text)
var/list/chars_to_add[max(charcount + 1, 0)]
return jointext(chars_to_add, char) + text
//Adds 'u' number of spaces ahead of the text 't'
/proc/add_lspace(t, u)
while(length(t) < u)
t = " [t]"
return t
//Adds 'u' number of spaces behind the text 't'
/proc/add_tspace(t, u)
while(length(t) < u)
t = "[t] "
return t
//Adds 'char' behind 'text' until there are 'count' characters total
/proc/add_trailing(text, count, char = " ")
var/charcount = count - length_char(text)
var/list/chars_to_add[max(charcount + 1, 0)]
return text + jointext(chars_to_add, char)
//Returns a string with reserved characters and spaces before the first letter removed
/proc/trim_left(text)
@@ -269,63 +254,45 @@
for (var/i = length(text), i > 0, i--)
if (text2ascii(text, i) > 32)
return copytext(text, 1, i + 1)
return ""
//Returns a string with reserved characters and spaces before the first word and after the last word removed.
/proc/trim(text, max_length)
if(max_length)
text = copytext(text, 1, max_length)
text = copytext_char(text, 1, max_length)
return trim_left(trim_right(text))
//Returns a string with the first element of the string capitalized.
/proc/capitalize(t as text)
return uppertext(copytext(t, 1, 2)) + copytext(t, 2)
//Centers text by adding spaces to either side of the string.
/proc/dd_centertext(message, length)
var/new_message = message
var/size = length(message)
var/delta = length - size
if(size == length)
return new_message
if(size > length)
return copytext(new_message, 1, length + 1)
if(delta == 1)
return new_message + " "
if(delta % 2)
new_message = " " + new_message
delta--
var/spaces = add_lspace("",delta/2-1)
return spaces + new_message + spaces
//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose
/proc/dd_limittext(message, length)
var/size = length(message)
if(size <= length)
return message
return copytext(message, 1, length + 1)
. = t[1]
return uppertext(.) + copytext(t, 1 + length(.))
/proc/stringmerge(text,compare,replace = "*")
//This proc fills in all spaces with the "replace" var (* by default) with whatever
//is in the other string at the same spot (assuming it is not a replace char).
//This is used for fingerprints
var/newtext = text
if(length(text) != length(compare))
return 0
for(var/i = 1, i < length(text), i++)
var/a = copytext(text,i,i+1)
var/b = copytext(compare,i,i+1)
var/text_it = 1 //iterators
var/comp_it = 1
var/newtext_it = 1
var/text_length = length(text)
var/comp_length = length(compare)
while(comp_it <= comp_length && text_it <= text_length)
var/a = text[text_it]
var/b = compare[comp_it]
//if it isn't both the same letter, or if they are both the replacement character
//(no way to know what it was supposed to be)
if(a != b)
if(a == replace) //if A is the replacement char
newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1)
newtext = copytext(newtext, 1, newtext_it) + b + copytext(newtext, newtext_it + length(newtext[newtext_it]))
else if(b == replace) //if B is the replacement char
newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1)
newtext = copytext(newtext, 1, newtext_it) + a + copytext(newtext, newtext_it + length(newtext[newtext_it]))
else //The lists disagree, Uh-oh!
return 0
text_it += length(a)
comp_it += length(b)
newtext_it += length(newtext[newtext_it])
return newtext
/proc/stringpercent(text,character = "*")
@@ -334,16 +301,21 @@
if(!text || !character)
return 0
var/count = 0
for(var/i = 1, i <= length(text), i++)
var/a = copytext(text,i,i+1)
var/lentext = length(text)
var/a = ""
for(var/i = 1, i <= lentext, i += length(a))
a = text[i]
if(a == character)
count++
return count
/proc/reverse_text(text = "")
var/new_text = ""
for(var/i = length(text); i > 0; i--)
new_text += copytext(text, i, i+1)
var/lentext = length(text)
var/letter = ""
for(var/i = 1, i <= lentext, i += length(letter))
letter = text[i]
new_text = letter + new_text
return new_text
GLOBAL_LIST_INIT(zero_character_only, list("0"))
@@ -366,15 +338,6 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
/proc/random_color()
return random_string(6, GLOB.hex_characters)
/proc/add_zero2(t, u)
var/temp1
while (length(t) < u)
t = "0[t]"
temp1 = t
if (length(t) > u)
temp1 = copytext(t,2,u+1)
return temp1
//merges non-null characters (3rd argument) from "from" into "into". Returns result
//e.g. into = "Hello World"
// from = "Seeya______"
@@ -387,41 +350,48 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
into = ""
if(!istext(from))
from = ""
var/null_ascii = istext(null_char) ? text2ascii(null_char,1) : null_char
var/previous = 0
var/null_ascii = istext(null_char) ? text2ascii(null_char, 1) : null_char
var/copying_into = FALSE
var/char = ""
var/start = 1
var/end = length(into) + 1
for(var/i=1, i<end, i++)
var/ascii = text2ascii(from, i)
if(ascii == null_ascii)
if(previous != 1)
. += copytext(from, start, i)
start = i
previous = 1
var/end_from = length(from)
var/end_into = length(into)
var/into_it = 1
var/from_it = 1
while(from_it <= end_from && into_it <= end_into)
char = from[from_it]
if(text2ascii(char) == null_ascii)
if(!copying_into)
. += copytext(from, start, from_it)
start = into_it
copying_into = TRUE
else
if(previous != 0)
. += copytext(into, start, i)
start = i
previous = 0
if(copying_into)
. += copytext(into, start, into_it)
start = from_it
copying_into = FALSE
into_it += length(into[into_it])
from_it += length(char)
if(previous == 0)
. += copytext(from, start, end)
if(copying_into)
. += copytext(into, start)
else
. += copytext(into, start, end)
. += copytext(from, start, from_it)
if(into_it <= end_into)
. += copytext(into, into_it)
//finds the first occurrence of one of the characters from needles argument inside haystack
//it may appear this can be optimised, but it really can't. findtext() is so much faster than anything you can do in byondcode.
//stupid byond :(
/proc/findchar(haystack, needles, start=1, end=0)
var/temp
var/char = ""
var/len = length(needles)
for(var/i=1, i<=len, i++)
temp = findtextEx(haystack, ascii2text(text2ascii(needles,i)), start, end) //Note: ascii2text(text2ascii) is faster than copytext()
if(temp)
end = temp
return end
for(var/i = 1, i <= len, i += length(char))
char = needles[i]
. = findtextEx(haystack, char, start, end)
if(.)
return
return 0
/proc/parsemarkdown_basic_step1(t, limited=FALSE)
if(length(t) <= 0)
@@ -579,23 +549,28 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
return t
#define string2charlist(string) (splittext(string, regex("(.)")) - splittext(string, ""))
/proc/text2charlist(text)
var/char = ""
var/lentext = length(text)
. = list()
for(var/i = 1, i <= lentext, i += length(char))
char = text[i]
. += char
/proc/rot13(text = "")
var/list/textlist = string2charlist(text)
var/list/result = list()
for(var/c in textlist)
var/ca = text2ascii(c)
if(ca >= text2ascii("a") && ca <= text2ascii("m"))
ca += 13
else if(ca >= text2ascii("n") && ca <= text2ascii("z"))
ca -= 13
else if(ca >= text2ascii("A") && ca <= text2ascii("M"))
ca += 13
else if(ca >= text2ascii("N") && ca <= text2ascii("Z"))
ca -= 13
result += ascii2text(ca)
return jointext(result, "")
var/lentext = length(text)
var/char = ""
var/ascii = 0
. = ""
for(var/i = 1, i <= lentext, i += length(char))
char = text[i]
ascii = text2ascii(char)
switch(ascii)
if(65 to 77, 97 to 109) //A to M, a to m
ascii += 13
if(78 to 90, 110 to 122) //N to Z, n to z
ascii -= 13
. += ascii2text(ascii)
//Takes a list of values, sanitizes it down for readability and character count,
//then exports it as a json file at data/npc_saves/[filename].json.
@@ -607,7 +582,8 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
return
//Regular expressions are, as usual, absolute magic
var/regex/all_invalid_symbols = new("\[^ -~]+")
//Any characters outside of 32 (space) to 126 (~) because treating things you don't understand as "magic" is really stupid
var/regex/all_invalid_symbols = new(@"[^ -~]{1}")
var/list/accepted = list()
for(var/string in proposed)
@@ -615,34 +591,44 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
continue
var/buffer = ""
var/early_culling = TRUE
for(var/pos = 1, pos <= length(string), pos++)
var/let = copytext(string, pos, (pos + 1) % length(string))
if(early_culling && !findtext(let,GLOB.is_alphanumeric))
var/lentext = length(string)
var/let = ""
for(var/pos = 1, pos <= lentext, pos += length(let))
let = string[pos]
if(!findtext(let, GLOB.is_alphanumeric))
continue
early_culling = FALSE
buffer += let
if(!findtext(buffer,GLOB.is_alphanumeric))
buffer = copytext(string, pos)
break
if(early_culling) //Never found any letters! Bail!
continue
var/punctbuffer = ""
var/cutoff = length(buffer)
for(var/pos = length(buffer), pos >= 0, pos--)
var/let = copytext(buffer, pos, (pos + 1) % length(buffer))
if(findtext(let,GLOB.is_alphanumeric))
var/cutoff = 0
lentext = length_char(buffer)
for(var/pos = 1, pos <= lentext, pos++)
let = copytext_char(buffer, -pos, -pos + 1)
if(!findtext(let, GLOB.is_punctuation)) //This won't handle things like Nyaaaa!~ but that's fine
break
if(findtext(let,GLOB.is_punctuation))
punctbuffer = let + punctbuffer //Note this isn't the same thing as using +=
cutoff = pos
punctbuffer += let
cutoff += length(let)
if(punctbuffer) //We clip down excessive punctuation to get the letter count lower and reduce repeats. It's not perfect but it helps.
var/exclaim = FALSE
var/question = FALSE
var/periods = 0
for(var/pos = length(punctbuffer), pos >= 0, pos--)
var/punct = copytext(punctbuffer, pos, (pos + 1) % length(punctbuffer))
if(!exclaim && findtext(punct,"!"))
lentext = length(punctbuffer)
for(var/pos = 1, pos <= lentext, pos += length(let))
let = punctbuffer[pos]
if(!exclaim && findtext(let, "!"))
exclaim = TRUE
if(!question && findtext(punct,"?"))
if(question)
break
if(!question && findtext(let, "?"))
question = TRUE
if(!exclaim && !question && findtext(punct,"."))
if(exclaim)
break
if(!exclaim && !question && findtext(let, ".")) //? and ! take priority over periods
periods += 1
if(exclaim)
if(question)
@@ -651,15 +637,13 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
punctbuffer = "!"
else if(question)
punctbuffer = "?"
else if(periods)
if(periods > 1)
punctbuffer = "..."
else
punctbuffer = "" //Grammer nazis be damned
buffer = copytext(buffer, 1, cutoff) + punctbuffer
if(!findtext(buffer,GLOB.is_alphanumeric))
continue
if(!buffer || length(buffer) > 280 || length(buffer) <= cullshort || buffer in accepted)
else if(periods > 1)
punctbuffer = "..."
else
punctbuffer = "" //Grammer nazis be damned
buffer = copytext(buffer, 1, -cutoff) + punctbuffer
lentext = length_char(buffer)
if(!buffer || lentext > 280 || lentext <= cullshort || (buffer in accepted))
continue
accepted += buffer
@@ -696,7 +680,7 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
var/leng = length(string)
var/next_space = findtext(string, " ", next_backslash + 1)
var/next_space = findtext(string, " ", next_backslash + length(string[next_backslash]))
if(!next_space)
next_space = leng - next_backslash
@@ -704,8 +688,8 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
return string
var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash)
var/macro = lowertext(copytext(string, next_backslash + 1, next_space))
var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1)
var/macro = lowertext(copytext(string, next_backslash + length(string[next_backslash]), next_space))
var/rest = next_backslash > leng ? "" : copytext(string, next_space + length(string[next_space]))
//See https://secure.byond.com/docs/ref/info.html#/DM/text/macros
switch(macro)
@@ -781,38 +765,35 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
return uppertext(pick(GLOB.alphabet))
/proc/unintelligize(message)
var/prefix=copytext(message,1,2)
var/regex/word_boundaries = regex(@"\b[\S]+\b", "g")
var/prefix = message[1]
if(prefix == ";")
message = copytext(message,2)
else if(prefix in list(":","#"))
prefix += copytext(message,2,3)
message = copytext(message,3)
message = copytext(message, 1 + length(prefix))
else if(prefix in list(":", "#"))
prefix += message[1 + length(prefix)]
message = copytext(message, length(prefix))
else
prefix=""
prefix = ""
var/list/words = splittext(message," ")
var/list/rearranged = list()
for(var/i=1;i<=words.len;i++)
var/cword = pick(words)
words.Remove(cword)
var/suffix = copytext(cword,length(cword)-1,length(cword))
while(length(cword)>0 && suffix in list(".",",",";","!",":","?"))
cword = copytext(cword,1 ,length(cword)-1)
suffix = copytext(cword,length(cword)-1,length(cword) )
while(word_boundaries.Find(message))
var/cword = word_boundaries.match
if(length(cword))
rearranged += cword
message = "[prefix][jointext(rearranged," ")]"
. = message
shuffle_inplace(rearranged)
return "[prefix][jointext(rearranged, " ")]"
/proc/readable_corrupted_text(text)
var/list/corruption_options = list("..", "£%", "~~\"", "!!", "*", "^", "$!", "-", "}", "?")
var/corrupted_text = ""
var/lentext = length(text)
var/letter = ""
// Have every letter have a chance of creating corruption on either side
// Small chance of letters being removed in place of corruption - still overall readable
for(var/letter_index = 1; letter_index <= length(text); letter_index++)
var/letter = text[letter_index]
for(var/letter_index = 1, letter_index <= lentext, letter_index += length(letter))
letter = text[letter_index]
if (prob(15))
corrupted_text += pick(corruption_options)
-23
View File
@@ -549,26 +549,3 @@
else //regex everything else (works for /proc too)
return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", ""))
/proc/strtohex(str)
if(!istext(str)||!str)
return
var/r
var/c
for(var/i = 1 to length(str))
c= text2ascii(str,i)
r+= num2hex(c)
return r
// Decodes hex to raw byte string.
// If safe=TRUE, returns null on incorrect input strings instead of CRASHing
/proc/hextostr(str, safe=FALSE)
if(!istext(str)||!str)
return
var/r
var/c
for(var/i = 1 to length(str)/2)
c = hex2num(copytext(str,i*2-1,i*2+1), safe)
if(isnull(c))
return null
r += ascii2text(c)
return r
+5 -5
View File
@@ -181,15 +181,15 @@ Turf and target are separate in case you want to teleport some distance from a t
//Returns whether or not a player is a guest using their ckey as an input
/proc/IsGuestKey(key)
if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx
return 0
return FALSE
var/i, ch, len = length(key)
for (i = 7, i <= len, ++i)
for (i = 7, i <= len, ++i) //we know the first 6 chars are Guest-
ch = text2ascii(key, i)
if (ch < 48 || ch > 57)
return 0
return 1
if (ch < 48 || ch > 57) //0-9
return FALSE
return TRUE
//Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame()
/mob/proc/apply_pref_name(role, client/C)
+20
View File
@@ -39,6 +39,26 @@
#error You need version 512 or higher
#endif
//Compatability -- These procs were added in 513.1493, not 513.1490
//Which really shoulda bumped us up to 514 right then and there but instead Lummox is a dumb dumb
#if DM_BUILD < 1493
#define length_char(args...) length(args)
#define text2ascii_char(args...) text2ascii(args)
#define copytext_char(args...) copytext(args)
#define splittext_char(args...) splittext(args)
#define spantext_char(args...) spantext(args)
#define nonspantext_char(args...) nonspantext(args)
#define findtext_char(args...) findtext(args)
#define findtextEx_char(args...) findtextEx(args)
#define findlasttext_char(args...) findlasttext(args)
#define findlasttextEx_char(args...) findlasttextEx(args)
#define replacetext_char(args...) replacetext(args)
#define replacetextEx_char(args...) replacetextEx(args)
// /regex procs
#define Find_char(args...) Find(args)
#define Replace_char(args...) Replace(args)
#endif
//Additional code for the above flags.
#ifdef TESTING
#warn compiling in TESTING mode. testing() debug messages will be visible.
+1
View File
@@ -10,6 +10,7 @@ GLOBAL_LIST_EMPTY(navbeacons) //list of all bot nagivation beacons, used
GLOBAL_LIST_EMPTY(teleportbeacons) //list of all tracking beacons used by teleporters
GLOBAL_LIST_EMPTY(deliverybeacons) //list of all MULEbot delivery beacons.
GLOBAL_LIST_EMPTY(deliverybeacontags) //list of all tags associated with delivery beacons.
GLOBAL_LIST_EMPTY(wayfindingbeacons) //list of all navigation beacons used by wayfinding pinpointers
GLOBAL_LIST_EMPTY(nuke_list)
GLOBAL_LIST_EMPTY(alarmdisplay) //list of all machines or programs that can display station alerts
GLOBAL_LIST_EMPTY(singularities) //list of all singularities on the station (actually technically all engines)
@@ -379,4 +379,8 @@
config_entry_value = 64
min_val = 0
/datum/config_entry/number/ratcap
config_entry_value = 64
min_val = 0
/datum/config_entry/flag/dynamic_config_enabled
+12 -1
View File
@@ -284,7 +284,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
log_world("ERROR: Station areas list failed to generate!")
/datum/controller/subsystem/mapping/proc/maprotate()
if(map_voted)
if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
return
var/players = GLOB.clients.len
@@ -307,9 +307,13 @@ GLOBAL_LIST_EMPTY(the_station_areas)
for (var/map in mapvotes)
if (!map)
mapvotes.Remove(map)
continue
if (!(map in global.config.maplist))
mapvotes.Remove(map)
continue
if(map in SSpersistence.blocked_maps)
mapvotes.Remove(map)
continue
var/datum/map_config/VM = global.config.maplist[map]
if (!VM)
mapvotes.Remove(map)
@@ -336,6 +340,13 @@ GLOBAL_LIST_EMPTY(the_station_areas)
if (. && VM.map_name != config.map_name)
to_chat(world, "<span class='boldannounce'>Map rotation has chosen [VM.map_name] for next round!</span>")
/datum/controller/subsystem/mapping/proc/mapvote()
if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
return
if(SSvote.mode) //Theres already a vote running, default to rotation.
maprotate()
SSvote.initiate_vote("map", "automatic map rotation")
/datum/controller/subsystem/mapping/proc/changemap(var/datum/map_config/VM)
if(!VM.MakeNextMap())
next_map_config = load_map_config(default_to_box = TRUE)
+1
View File
@@ -8,6 +8,7 @@ SUBSYSTEM_DEF(mobs)
var/static/list/clients_by_zlevel[][]
var/static/list/dead_players_by_zlevel[][] = list(list()) // Needs to support zlevel 1 here, MaxZChanged only happens when z2 is created and new_players can login before that.
var/static/list/cubemonkeys = list()
var/static/list/cheeserats = list()
/datum/controller/subsystem/mobs/stat_entry()
..("P:[GLOB.mob_living_list.len]")
+23 -2
View File
@@ -1,6 +1,8 @@
#define FILE_ANTAG_REP "data/AntagReputation.json"
#define FILE_RECENT_MAPS "data/RecentMaps.json"
#define KEEP_ROUNDS_MAP 3
SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
@@ -9,7 +11,8 @@ SUBSYSTEM_DEF(persistence)
var/list/obj/structure/chisel_message/chisel_messages = list()
var/list/saved_messages = list()
var/list/saved_modes = list(1,2,3)
var/list/saved_maps = list(1,2)
var/list/saved_maps = list()
var/list/blocked_maps = list()
var/list/saved_trophies = list()
var/list/antag_rep = list()
var/list/antag_rep_change = list()
@@ -116,6 +119,18 @@ SUBSYSTEM_DEF(persistence)
return
saved_maps = json["data"]
//Convert the mapping data to a shared blocking list, saves us doing this in several places later.
for(var/map in config.maplist)
var/datum/map_config/VM = config.maplist[map]
var/run = 0
if(VM.map_name == SSmapping.config.map_name)
run++
for(var/name in SSpersistence.saved_maps)
if(VM.map_name == name)
run++
if(run >= 2) //If run twice in the last KEEP_ROUNDS_MAP + 1 (including current) rounds, disable map for voting and rotation.
blocked_maps += VM.map_name
/datum/controller/subsystem/persistence/proc/LoadAntagReputation()
var/json = file2text(FILE_ANTAG_REP)
if(!json)
@@ -284,7 +299,13 @@ SUBSYSTEM_DEF(persistence)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectMaps()
saved_maps[2] = saved_maps[1]
if(length(saved_maps) > KEEP_ROUNDS_MAP) //Get rid of extras from old configs.
saved_maps.Cut(KEEP_ROUNDS_MAP+1)
var/mapstosave = min(length(saved_maps)+1, KEEP_ROUNDS_MAP)
if(length(saved_maps) < mapstosave) //Add extras if too short, one per round.
saved_maps += mapstosave
for(var/i = mapstosave; i > 1; i--)
saved_maps[i] = saved_maps[i-1]
saved_maps[1] = SSmapping.config.map_name
var/json_file = file(FILE_RECENT_MAPS)
var/list/file_data = list()
@@ -41,3 +41,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
badquirk = TRUE
if(badquirk)
cli.prefs.save_character()
// Assign wayfinding pinpointer granting quirk if they're new
if(cli.calc_exp_type(EXP_TYPE_LIVING) < 1200 && !user.has_quirk(/datum/quirk/needswayfinder))
user.add_quirk(/datum/quirk/needswayfinder, TRUE)
+18 -30
View File
@@ -43,8 +43,6 @@ SUBSYSTEM_DEF(ticker)
var/queue_delay = 0
var/list/queued_players = list() //used for join queues when the server exceeds the hard population cap
var/maprotatechecked = 0
var/news_report
var/late_join_disabled
@@ -156,10 +154,10 @@ SUBSYSTEM_DEF(ticker)
//lobby stats for statpanels
if(isnull(timeLeft))
timeLeft = max(0,start_at - world.time)
totalPlayers = LAZYLEN(GLOB.new_player_list)
totalPlayers = LAZYLEN(GLOB.new_player_list)
totalPlayersReady = 0
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
if(player.ready == PLAYER_READY_TO_PLAY)
++totalPlayersReady
@@ -192,13 +190,13 @@ SUBSYSTEM_DEF(ticker)
if(GAME_STATE_PLAYING)
mode.process(wait * 0.1)
check_queue()
check_maprotate()
if(!roundend_check_paused && mode.check_finished(force_ending) || force_ending)
current_state = GAME_STATE_FINISHED
toggle_ooc(TRUE) // Turn it on
toggle_dooc(TRUE)
declare_completion(force_ending)
check_maprotate()
Master.SetRunLevel(RUNLEVEL_POSTGAME)
@@ -343,8 +341,8 @@ SUBSYSTEM_DEF(ticker)
explosion(epi, 0, 256, 512, 0, TRUE, TRUE, 0, TRUE)
/datum/controller/subsystem/ticker/proc/create_characters()
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
GLOB.joined_player_list += player.ckey
player.create_character(FALSE)
@@ -353,8 +351,8 @@ SUBSYSTEM_DEF(ticker)
CHECK_TICK
/datum/controller/subsystem/ticker/proc/collect_minds()
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/P = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/P = i
if(P.new_character && P.new_character.mind)
SSticker.minds += P.new_character.mind
CHECK_TICK
@@ -362,8 +360,8 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/proc/equip_characters()
var/captainless=1
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/N = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/N = i
var/mob/living/carbon/human/player = N.new_character
if(istype(player) && player.mind && player.mind.assigned_role)
if(player.mind.assigned_role == "Captain")
@@ -374,16 +372,16 @@ SUBSYSTEM_DEF(ticker)
SSquirks.AssignQuirks(N.new_character, N.client, TRUE)
CHECK_TICK
if(captainless)
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/N = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/N = i
if(N.new_character)
to_chat(N, "<span class='notice'>Captainship not forced on anyone.</span>")
CHECK_TICK
/datum/controller/subsystem/ticker/proc/transfer_characters()
var/list/livings = list()
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
var/mob/living = player.transfer_character()
if(living)
qdel(player)
@@ -449,17 +447,9 @@ SUBSYSTEM_DEF(ticker)
queue_delay = 0
/datum/controller/subsystem/ticker/proc/check_maprotate()
if (!CONFIG_GET(flag/maprotation))
if(!CONFIG_GET(flag/maprotation))
return
if (SSshuttle.emergency && SSshuttle.emergency.mode != SHUTTLE_ESCAPE || SSshuttle.canRecall())
return
if (maprotatechecked)
return
maprotatechecked = 1
//map rotate chance defaults to 75% of the length of the round (in minutes)
if (!prob((world.time/600)*CONFIG_GET(number/maprotatechancedelta)))
if(world.time - SSticker.round_start_time < 10 MINUTES) //Not forcing map rotation for very short rounds.
return
INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/.proc/maprotate)
@@ -493,12 +483,10 @@ SUBSYSTEM_DEF(ticker)
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
maprotatechecked = SSticker.maprotatechecked
round_start_time = SSticker.round_start_time
queue_delay = SSticker.queue_delay
queued_players = SSticker.queued_players
maprotatechecked = SSticker.maprotatechecked
switch (current_state)
if(GAME_STATE_SETTING_UP)
@@ -569,8 +557,8 @@ SUBSYSTEM_DEF(ticker)
//Everyone who wanted to be an observer gets made one now
/datum/controller/subsystem/ticker/proc/create_observers()
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind)
//Break chain since this has a sleep input in it
addtimer(CALLBACK(player, /mob/dead/new_player.proc/make_me_an_observer), 1)
+14 -15
View File
@@ -74,14 +74,16 @@ SUBSYSTEM_DEF(vote)
else if(mode == "map")
for (var/non_voter_ckey in non_voters)
var/client/C = non_voters[non_voter_ckey]
if(C.prefs.preferred_map && choices[C.prefs.preferred_map]) //No votes if the map isnt in the vote.
var/preferred_map = C.prefs.preferred_map
choices[preferred_map] += 1
greatest_votes = max(greatest_votes, choices[preferred_map])
else if(config.defaultmap && choices[config.defaultmap]) //No votes if the map isnt in the vote.
var/default_map = config.defaultmap.map_name
choices[default_map] += 1
greatest_votes = max(greatest_votes, choices[default_map])
if(C.prefs.preferred_map)
if(choices[C.prefs.preferred_map]) //No votes if the map isnt in the vote.
var/preferred_map = C.prefs.preferred_map
choices[preferred_map] += 1
greatest_votes = max(greatest_votes, choices[preferred_map])
else if(config.defaultmap)
if(choices[config.defaultmap]) //No votes if the map isnt in the vote.
var/default_map = config.defaultmap.map_name
choices[default_map] += 1
greatest_votes = max(greatest_votes, choices[default_map])
//get all options with that many votes and return them in a list
. = list()
if(greatest_votes)
@@ -163,6 +165,9 @@ SUBSYSTEM_DEF(vote)
return FALSE
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key)
if(!Master.current_runlevel) //Server is still intializing.
to_chat(usr, "<span class='warning'>Cannot start vote, server is not done initializing.</span>")
return FALSE
var/admin = FALSE
var/ckey = ckey(initiator_key)
if(GLOB.admin_datums[ckey])
@@ -192,13 +197,7 @@ SUBSYSTEM_DEF(vote)
return FALSE
for(var/map in config.maplist)
var/datum/map_config/VM = config.maplist[map]
var/run = 0
if(VM.map_name == SSmapping.config.map_name)
run++
for(var/name in SSpersistence.saved_maps)
if(VM.map_name == name)
run++
if(run >= 2 || !VM.votable) //If run twice in the last three (including current) as of time of writing, disable map for voting.
if(!VM.votable || (VM.map_name in SSpersistence.blocked_maps))
continue
choices.Add(VM.map_name)
if("custom")
+12 -10
View File
@@ -199,14 +199,16 @@
var/list/new_message = list()
for(var/word in message_split)
var/suffix = copytext(word,-1)
// Check if we have a suffix and break it out of the word
if(suffix in list("." , "," , ";" , "!" , ":" , "?"))
word = copytext(word,1,-1)
else
suffix = ""
var/suffix = ""
var/suffix_foundon = 0
for(var/potential_suffix in list("." , "," , ";" , "!" , ":" , "?"))
suffix_foundon = findtext(word, potential_suffix, -length(potential_suffix))
if(suffix_foundon)
suffix = potential_suffix
break
if(suffix_foundon)
word = copytext(word, 1, suffix_foundon)
word = html_decode(word)
if(lowertext(word) in common_words)
@@ -216,10 +218,10 @@
new_message += pick("uh","erm")
break
else
var/list/charlist = string2charlist(word) // Stupid shit code
var/list/charlist = text2charlist(word)
charlist.len = round(charlist.len * 0.5, 1)
shuffle_inplace(charlist)
charlist.len = round(charlist.len * 0.5,1)
new_message += html_encode(jointext(charlist,"")) + suffix
new_message += jointext(charlist, "") + suffix
message = jointext(new_message, " ")
+4 -3
View File
@@ -248,7 +248,7 @@
SetSpread(DISEASE_SPREAD_CONTACT_FLUIDS)
else
SetSpread(DISEASE_SPREAD_BLOOD)
permeability_mod = max(CEILING(0.4 * properties["transmittable"], 1), 1)
cure_chance = 15 - CLAMP(properties["resistance"], -5, 5) // can be between 10 and 20
stage_prob = max(properties["stage_rate"], 2)
@@ -419,7 +419,7 @@
// Should be only 1 entry left, but if not let's only return a single entry
var/datum/disease/advance/to_return = pick(diseases)
to_return.Refresh(1)
to_return.Refresh(TRUE)
return to_return
/proc/SetViruses(datum/reagent/R, list/data)
@@ -464,8 +464,9 @@
var/new_name = stripped_input(user, "Name your new disease.", "New Name")
if(!new_name)
return
D.AssignName(new_name)
D.Refresh()
D.AssignName(new_name) //Updates the master copy
D.name = new_name //Updates our copy
var/list/targets = list("Random")
targets += sortNames(GLOB.human_list)
+20 -18
View File
@@ -1,6 +1,6 @@
/*! Material datum
Simple datum which is instanced once per type and is used for every object of said material. It has a variety of variables that define behavior. Subtyping from this makes it easier to create your own materials.
Simple datum which is instanced once per type and is used for every object of said material. It has a variety of variables that define behavior. Subtyping from this makes it easier to create your own materials.
*/
@@ -27,7 +27,7 @@ Simple datum which is instanced once per type and is used for every object of sa
///Armor modifiers, multiplies an items normal armor vars by these amounts.
var/armor_modifiers = list("melee" = 1, "bullet" = 1, "laser" = 1, "energy" = 1, "bomb" = 1, "bio" = 1, "rad" = 1, "fire" = 1, "acid" = 1)
///How beautiful is this material per unit
var/beauty_modifier = 0
var/beauty_modifier = 0
///This proc is called when the material is added to an object.
/datum/material/proc/on_applied(atom/source, amount, material_flags)
@@ -48,20 +48,21 @@ Simple datum which is instanced once per type and is used for every object of sa
///This proc is called when the material is added to an object specifically.
/datum/material/proc/on_applied_obj(var/obj/o, amount, material_flags)
var/new_max_integrity = CEILING(o.max_integrity * integrity_modifier, 1)
o.modify_max_integrity(new_max_integrity)
o.force *= strength_modifier
o.throwforce *= strength_modifier
if(material_flags & MATERIAL_AFFECT_STATISTICS)
var/new_max_integrity = CEILING(o.max_integrity * integrity_modifier, 1)
o.modify_max_integrity(new_max_integrity)
o.force *= strength_modifier
o.throwforce *= strength_modifier
var/list/temp_armor_list = list() //Time to add armor modifiers!
var/list/temp_armor_list = list() //Time to add armor modifiers!
if(!istype(o.armor))
return
var/list/current_armor = o.armor?.getList()
if(!istype(o.armor))
return
var/list/current_armor = o.armor?.getList()
for(var/i in current_armor)
temp_armor_list[i] = current_armor[i] * armor_modifiers[i]
o.armor = getArmor(arglist(temp_armor_list))
for(var/i in current_armor)
temp_armor_list[i] = current_armor[i] * armor_modifiers[i]
o.armor = getArmor(arglist(temp_armor_list))
///This proc is called when the material is removed from an object.
/datum/material/proc/on_removed(atom/source, material_flags)
@@ -72,13 +73,14 @@ Simple datum which is instanced once per type and is used for every object of sa
if(material_flags & MATERIAL_ADD_PREFIX)
source.name = initial(source.name)
if(istype(source, /obj)) //objs
on_removed_obj(source, material_flags)
///This proc is called when the material is removed from an object specifically.
/datum/material/proc/on_removed_obj(var/obj/o, amount, material_flags)
var/new_max_integrity = initial(o.max_integrity)
o.modify_max_integrity(new_max_integrity)
o.force = initial(o.force)
o.throwforce = initial(o.throwforce)
if(material_flags & MATERIAL_AFFECT_STATISTICS)
var/new_max_integrity = initial(o.max_integrity)
o.modify_max_integrity(new_max_integrity)
o.force = initial(o.force)
o.throwforce = initial(o.throwforce)
+3 -3
View File
@@ -153,10 +153,10 @@
else
to_chat(current, "<span class='warning'>I feel like I've become worse at [S.name]!</span>")
///Gets the skill's singleton and returns the result of its get_skill_speed_modifier
/datum/mind/proc/get_skill_speed_modifier(skill)
///Gets the skill's singleton and returns the result of its get_skill_modifier
/datum/mind/proc/get_skill_modifier(skill, modifier)
var/datum/skill/S = GetSkillRef(skill)
return S.get_skill_speed_modifier(known_skills[S] || SKILL_LEVEL_NONE)
return S.get_skill_modifier(modifier, known_skills[S] || SKILL_LEVEL_NONE)
/datum/mind/proc/get_skill_level(skill)
var/datum/skill/S = GetSkillRef(skill)
+3 -2
View File
@@ -1,6 +1,7 @@
/datum/skill
var/name = "Skill"
var/desc = "the art of doing things"
var/modifiers = list(SKILL_SPEED_MODIFIER = list(1, 1, 1, 1, 1, 1, 1)) //Dictionary of modifier type - list of modifiers (indexed by level). 7 entries in each list for all 7 skill levels.
/datum/skill/proc/get_skill_speed_modifier(level)
return
/datum/skill/proc/get_skill_modifier(modifier, level)
return modifiers[modifier][level+1] //+1 because lists start at 1
+1 -17
View File
@@ -1,20 +1,4 @@
/datum/skill/medical
name = "Medical"
desc = "From Bandaids to biopsies, this improves your ability to get people back up both in the field and on the operating table."
/datum/skill/medical/get_skill_speed_modifier(level)
switch(level)
if(SKILL_LEVEL_NONE)
return 1
if(SKILL_LEVEL_NOVICE)
return 0.95
if(SKILL_LEVEL_APPRENTICE)
return 0.9
if(SKILL_LEVEL_JOURNEYMAN)
return 0.85
if(SKILL_LEVEL_EXPERT)
return 0.75
if(SKILL_LEVEL_MASTER)
return 0.6
if(SKILL_LEVEL_LEGENDARY)
return 0.5
modifiers = list(SKILL_SPEED_MODIFIER = list(1, 0.95, 0.9, 0.85, 0.75, 0.6, 0.5))
+1 -17
View File
@@ -1,20 +1,4 @@
/datum/skill/mining
name = "Mining"
desc = "A dwarf's biggest skill, after drinking."
/datum/skill/mining/get_skill_speed_modifier(level)
switch(level)
if(SKILL_LEVEL_NONE)
return 1
if(SKILL_LEVEL_NOVICE)
return 0.95
if(SKILL_LEVEL_APPRENTICE)
return 0.9
if(SKILL_LEVEL_JOURNEYMAN)
return 0.85
if(SKILL_LEVEL_EXPERT)
return 0.75
if(SKILL_LEVEL_MASTER)
return 0.65
if(SKILL_LEVEL_LEGENDARY)
return 0.5
modifiers = list(SKILL_SPEED_MODIFIER = list(1, 0.95, 0.9, 0.85, 0.75, 0.6, 0.5))
+32 -1
View File
@@ -129,8 +129,39 @@
/datum/quirk/phobia/post_add()
var/mob/living/carbon/human/H = quirk_holder
H.gain_trauma(new /datum/brain_trauma/mild/phobia(H.client.prefs.phobia), TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/phobia/remove()
var/mob/living/carbon/human/H = quirk_holder
if(H)
H.cure_trauma_type(/datum/brain_trauma/mild/phobia, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/needswayfinder
name = "Navigationally Challenged"
desc = "Lacking familiarity with certain stations, you start with a wayfinding pinpointer where available."
value = 0
medical_record_text = "Patient demonstrates a keen ability to get lost."
var/obj/item/pinpointer/wayfinding/wayfinder
var/where
/datum/quirk/needswayfinder/on_spawn()
if(!GLOB.wayfindingbeacons.len)
return
var/mob/living/carbon/human/H = quirk_holder
wayfinder = new /obj/item/pinpointer/wayfinding
var/list/slots = list(
"in your left pocket" = ITEM_SLOT_LPOCKET,
"in your right pocket" = ITEM_SLOT_RPOCKET,
"in your backpack" = ITEM_SLOT_BACKPACK
)
where = H.equip_in_one_of_slots(wayfinder, slots, FALSE) || "at your feet"
/datum/quirk/needswayfinder/post_add()
if(!GLOB.wayfindingbeacons.len)
return
if(where == "in your backpack")
var/mob/living/carbon/human/H = quirk_holder
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
to_chat(quirk_holder, "<span class='notice'>There is a pinpointer [where], which can help you find your way around. Click in-hand to activate.</span>")
+1 -1
View File
@@ -120,7 +120,7 @@
return TRUE
/datum/wires/proc/is_dud(wire)
return dd_hasprefix(wire, WIRE_DUD_PREFIX)
return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1)
/datum/wires/proc/is_dud_color(color)
return is_dud(get_wire(color))
@@ -433,7 +433,7 @@
var/dat = ""
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
var/timeleft = SSshuttle.emergency.timeLeft()
dat += "<B>Emergency shuttle</B>\n<BR>\nETA: [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]"
dat += "<B>Emergency shuttle</B>\n<BR>\nETA: [timeleft / 60 % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]"
var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500)
+1 -1
View File
@@ -165,7 +165,7 @@
if(timing)
var/disp1 = id
var/time_left = time_left(seconds = TRUE)
var/disp2 = "[add_zero(num2text((time_left / 60) % 60),2)]:[add_zero(num2text(time_left % 60), 2)]"
var/disp2 = "[add_leading(num2text((time_left / 60) % 60), 2, "0")]:[add_leading(num2text(time_left % 60), 2, "0")]"
if(length(disp2) > CHARS_PER_LINE)
disp2 = "Error"
update_display(disp1, disp2)
+37 -11
View File
@@ -6,7 +6,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "navbeacon0-f"
name = "navigation beacon"
desc = "A radio beacon used for bot navigation."
desc = "A radio beacon used for bot navigation and crew wayfinding."
level = 1 // underfloor
layer = LOW_OBJ_LAYER
max_integrity = 500
@@ -18,28 +18,31 @@
var/location = "" // location response text
var/list/codes // assoc. list of transponder codes
var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value"
var/wayfinding = FALSE
req_one_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS)
/obj/machinery/navbeacon/Initialize()
. = ..()
if(wayfinding)
if(!location)
var/obj/machinery/door/airlock/A = locate(/obj/machinery/door/airlock) in loc
if(A)
location = A.name
else
location = get_area(src)
codes_txt += "wayfinding=[location]"
set_codes()
var/turf/T = loc
hide(T.intact)
if(codes["patrol"])
if(!GLOB.navbeacons["[z]"])
GLOB.navbeacons["[z]"] = list()
GLOB.navbeacons["[z]"] += src //Register with the patrol list!
if(codes["delivery"])
GLOB.deliverybeacons += src
GLOB.deliverybeacontags += location
glob_lists_register(init=TRUE)
/obj/machinery/navbeacon/Destroy()
if (GLOB.navbeacons["[z]"])
GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one.
GLOB.deliverybeacons -= src
glob_lists_deregister()
return ..()
/obj/machinery/navbeacon/onTransitZ(old_z, new_z)
@@ -67,6 +70,25 @@
else
codes[e] = "1"
/obj/machinery/navbeacon/proc/glob_lists_deregister()
if (GLOB.navbeacons["[z]"])
GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one.
GLOB.deliverybeacons -= src
GLOB.deliverybeacontags -= location
GLOB.wayfindingbeacons -= src
/obj/machinery/navbeacon/proc/glob_lists_register(var/init=FALSE)
if(!init)
glob_lists_deregister()
if(codes["patrol"])
if(!GLOB.navbeacons["[z]"])
GLOB.navbeacons["[z]"] = list()
GLOB.navbeacons["[z]"] += src //Register with the patrol list!
if(codes["delivery"])
GLOB.deliverybeacons += src
GLOB.deliverybeacontags += location
if(codes["wayfinding"])
GLOB.wayfindingbeacons += src
// called when turf state changes
// hide the object if turf is intact
@@ -170,6 +192,7 @@ Transponder Codes:<UL>"}
var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN)
if(newloc)
location = newloc
glob_lists_register()
updateDialog()
else if(href_list["edit"])
@@ -187,12 +210,14 @@ Transponder Codes:<UL>"}
codes.Remove(codekey)
codes[newkey] = newval
glob_lists_register()
updateDialog()
else if(href_list["delete"])
var/codekey = href_list["code"]
codes.Remove(codekey)
glob_lists_register()
updateDialog()
else if(href_list["add"])
@@ -210,5 +235,6 @@ Transponder Codes:<UL>"}
codes = new()
codes[newkey] = newval
glob_lists_register()
updateDialog()
@@ -399,7 +399,7 @@
/obj/item/mecha_parts/mecha_equipment/generator/get_equip_info()
var/output = ..()
if(output)
return "[output] \[[fuel]: [round(fuel.amount*fuel.mats_per_stack,0.1)] cm<sup>3</sup>\] - <a href='?src=[REF(src)];toggle=1'>[equip_ready?"A":"Dea"]ctivate</a>"
return "[output] \[[fuel]: [round(fuel.amount*MINERAL_MATERIAL_AMOUNT,0.1)] cm<sup>3</sup>\] - <a href='?src=[REF(src)];toggle=1'>[equip_ready?"A":"Dea"]ctivate</a>"
/obj/item/mecha_parts/mecha_equipment/generator/action(target)
if(chassis)
@@ -409,9 +409,9 @@
/obj/item/mecha_parts/mecha_equipment/generator/proc/load_fuel(var/obj/item/stack/sheet/P)
if(P.type == fuel.type && P.amount > 0)
var/to_load = max(max_fuel - fuel.amount*fuel.mats_per_stack,0)
var/to_load = max(max_fuel - fuel.amount*MINERAL_MATERIAL_AMOUNT,0)
if(to_load)
var/units = min(max(round(to_load / P.mats_per_stack),1),P.amount)
var/units = min(max(round(to_load / MINERAL_MATERIAL_AMOUNT),1),P.amount)
fuel.amount += units
P.use(units)
occupant_message("<span class='notice'>[units] unit\s of [fuel] successfully loaded.</span>")
@@ -447,7 +447,7 @@
if(cur_charge < chassis.cell.maxcharge)
use_fuel = fuel_per_cycle_active
chassis.give_power(power_per_cycle)
fuel.amount -= min(use_fuel/fuel.mats_per_stack,fuel.amount)
fuel.amount -= min(use_fuel/MINERAL_MATERIAL_AMOUNT,fuel.amount)
update_equip_info()
return 1
+25 -29
View File
@@ -944,17 +944,15 @@
else
return 0
/obj/mecha/proc/mmi_move_inside(obj/item/mmi/mmi_as_oc, mob/user)
if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client)
to_chat(user, "<span class='warning'>Consciousness matrix not detected!</span>")
/obj/mecha/proc/mmi_move_inside(obj/item/mmi/M, mob/user)
if(!M.brain_check(user))
return FALSE
else if(mmi_as_oc.brainmob.stat)
to_chat(user, "<span class='warning'>Beta-rhythm below acceptable level!</span>")
return FALSE
else if(occupant)
var/mob/living/brain/B = M.brainmob
if(occupant)
to_chat(user, "<span class='warning'>Occupant detected!</span>")
return FALSE
else if(dna_lock && (!mmi_as_oc.brainmob.stored_dna || (dna_lock != mmi_as_oc.brainmob.stored_dna.unique_enzymes)))
if(dna_lock && (!B.stored_dna || (dna_lock != B.stored_dna.unique_enzymes)))
to_chat(user, "<span class='warning'>Access denied. [name] is secured with a DNA lock.</span>")
return FALSE
@@ -962,42 +960,40 @@
if(do_after(user, 40, target = src))
if(!occupant)
return mmi_moved_inside(mmi_as_oc, user)
return mmi_moved_inside(M, user)
else
to_chat(user, "<span class='warning'>Occupant detected!</span>")
else
to_chat(user, "<span class='notice'>You stop inserting the MMI.</span>")
return FALSE
/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc, mob/user)
if(!(Adjacent(mmi_as_oc) && Adjacent(user)))
/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/M, mob/user)
if(!(Adjacent(M) && Adjacent(user)))
return FALSE
if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client)
to_chat(user, "<span class='notice'>Consciousness matrix not detected!</span>")
if(!M.brain_check(user))
return FALSE
else if(mmi_as_oc.brainmob.stat)
to_chat(user, "<span class='warning'>Beta-rhythm below acceptable level!</span>")
var/mob/living/brain/B = M.brainmob
if(!user.transferItemToLoc(M, src))
to_chat(user, "<span class='warning'>\the [M] is stuck to your hand, you cannot put it in \the [src]!</span>")
return FALSE
if(!user.transferItemToLoc(mmi_as_oc, src))
to_chat(user, "<span class='warning'>\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]!</span>")
return FALSE
var/mob/living/brainmob = mmi_as_oc.brainmob
mmi_as_oc.mecha = src
occupant = brainmob
M.mecha = src
occupant = B
silicon_pilot = TRUE
brainmob.forceMove(src) //should allow relaymove
brainmob.reset_perspective(src)
brainmob.remote_control = src
brainmob.update_mobility()
brainmob.update_mouse_pointer()
B.forceMove(src) //should allow relaymove
B.reset_perspective(src)
B.remote_control = src
B.update_mobility()
B.update_mouse_pointer()
icon_state = initial(icon_state)
update_icon()
setDir(dir_in)
log_message("[mmi_as_oc] moved in as pilot.", LOG_MECHA)
log_message("[M] moved in as pilot.", LOG_MECHA)
if(!internal_damage)
SEND_SOUND(occupant, sound('sound/mecha/nominal.ogg',volume=50))
GrantActions(brainmob)
log_game("[key_name(user)] has put the MMI/posibrain of [key_name(brainmob)] into [src] at [AREACOORD(src)]")
GrantActions(B)
log_game("[key_name(user)] has put the MMI/posibrain of [key_name(B)] into [src] at [AREACOORD(src)]")
return TRUE
/obj/mecha/container_resist(mob/living/user)
+2 -2
View File
@@ -23,9 +23,9 @@
hud.remove_hud_from(L)
..()
/obj/mecha/medical/odysseus/mmi_moved_inside(obj/item/mmi/mmi_as_oc, mob/user)
/obj/mecha/medical/odysseus/mmi_moved_inside(obj/item/mmi/M, mob/user)
. = ..()
if(.)
var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
var/mob/living/brain/B = mmi_as_oc.brainmob
var/mob/living/brain/B = M.brainmob
hud.add_hud_to(B)
@@ -172,7 +172,7 @@
/obj/effect/particle_effect/foam/proc/spread_foam()
var/turf/t_loc = get_turf(src)
for(var/turf/T in t_loc.GetAtmosAdjacentTurfs())
for(var/turf/T in t_loc.reachableAdjacentTurfs())
var/obj/effect/particle_effect/foam/foundfoam = locate() in T //Don't spread foam where there's already foam!
if(foundfoam)
continue
@@ -238,7 +238,7 @@
amount = round(sqrt(amt / 2), 1)
carry.copy_to(chemholder, carry.total_volume)
if(metaltype != 0)
if(metaltype)
metal = metaltype
/datum/effect_system/foam_spread/start()
+1 -1
View File
@@ -765,7 +765,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(tool_behaviour == TOOL_MINING && ishuman(user))
var/mob/living/carbon/human/H = user
skill_modifier = H.mind.get_skill_speed_modifier(/datum/skill/mining)
skill_modifier = H.mind.get_skill_modifier(/datum/skill/mining, SKILL_SPEED_MODIFIER)
delay *= toolspeed * skill_modifier
+1 -1
View File
@@ -187,7 +187,7 @@ RLD
icon_state = "rcd"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
custom_price = 1700
custom_premium_price = 1700
max_matter = 160
slot_flags = ITEM_SLOT_BELT
item_flags = NO_MAT_REDEMPTION | NOBLUDGEON
+8 -7
View File
@@ -405,8 +405,8 @@ update_label()
if (isnull(input_text))
return
var/t = copytext(sanitize(input_text), 1, 26)
if(!t || t == "Unknown" || t == "floor" || t == "wall" || t == "r-wall") //Same as mob/dead/new_player/prefrences.dm
var/target_name = copytext(sanitize(input_text), 1, 26)
if(!target_name || target_name == "Unknown" || target_name == "floor" || target_name == "wall" || target_name == "r-wall") //Same as mob/dead/new_player/preferences.dm
if (ishuman(user))
var/mob/living/carbon/human/human_agent = user
@@ -421,17 +421,17 @@ update_label()
alert ("Invalid name.")
return
else
registered_name = t
registered_name = target_name
var/u = copytext(sanitize(input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than Maintenance.", "Agent card job assignment", assignment ? assignment : "Assistant") as text | null),1,MAX_MESSAGE_LEN)
if(!u)
var/target_occupation = copytext(sanitize(input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than Maintenance.", "Agent card job assignment", assignment ? assignment : "Assistant") as text | null),1,MAX_MESSAGE_LEN)
if(!target_occupation)
registered_name = ""
return
assignment = u
assignment = target_occupation
update_label()
forged = TRUE
to_chat(user, "<span class='notice'>You successfully forge the ID card.</span>")
log_game("[key_name(user)] has forged \the [initial(name)] with name \"[registered_name]\" and occupation \"[assignment]\".")
// First time use automatically sets the account id to the user.
if (first_use && !registered_account)
@@ -448,6 +448,7 @@ update_label()
else if (popup_input == "Forge/Reset" && forged)
registered_name = initial(registered_name)
assignment = initial(assignment)
log_game("[key_name(user)] has reset \the [initial(name)] named \"[src]\" to default.")
update_label()
forged = FALSE
to_chat(user, "<span class='notice'>You successfully reset the ID card.</span>")
+7 -3
View File
@@ -306,10 +306,14 @@ Code:
var/list/S = list(" Off","AOff"," On", " AOn")
var/list/chg = list("N","C","F")
//Neither copytext nor copytext_char is appropriate here; neither 30 UTF-8 code units nor 30 code points equates to 30 columns of output.
//Some glyphs are very tall or very wide while others are small or even take up no space at all.
//Emojis can take modifiers which are many characters but render as only one glyph.
//A proper solution here (as far as Unicode goes, maybe not ideal as far as markup goes, a table would be better)
//would be to use <span style="width: NNNpx; overflow: none;">[A.area.name]</span>
for(var/obj/machinery/power/apc/A in L)
menu += copytext(add_tspace(A.area.name, 30), 1, 30)
menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(DisplayPower(A.lastused_total), 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]<BR>"
menu += copytext_char(add_trailing(A.area.name, 30, " "), 1, 30)
menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_leading(DisplayPower(A.lastused_total), 6, " ")] [A.cell ? "[add_leading(round(A.cell.percent()), 3, " ")]% [chg[A.charging+1]]" : " N/C"]<BR>"
menu += "</FONT></PRE>"
+6 -26
View File
@@ -243,39 +243,20 @@
if(!isturf(loc))
to_chat(user, "<span class='warning'>You can't put [M] in, the frame has to be standing on the ground to be perfectly precise!</span>")
return
if(!M.brainmob)
to_chat(user, "<span class='warning'>Sticking an empty [M.name] into the frame would sort of defeat the purpose!</span>")
if(!M.brain_check(user))
return
var/mob/living/brain/BM = M.brainmob
if(!BM.key || !BM.mind)
to_chat(user, "<span class='warning'>The MMI indicates that their mind is completely unresponsive; there's no point!</span>")
return
if(!BM.client) //braindead
to_chat(user, "<span class='warning'>The MMI indicates that their mind is currently inactive; it might change!</span>")
return
if(BM.stat == DEAD || BM.suiciding || (M.brain && (M.brain.brain_death || M.brain.suicided)))
to_chat(user, "<span class='warning'>Sticking a dead brain into the frame would sort of defeat the purpose!</span>")
return
if(M.brain?.organ_flags & ORGAN_FAILING)
to_chat(user, "<span class='warning'>The MMI indicates that the brain is damaged!</span>")
return
if(is_banned_from(BM.ckey, "Cyborg") || QDELETED(src) || QDELETED(BM) || QDELETED(user) || QDELETED(M) || !Adjacent(user))
var/mob/living/brain/B = M.brainmob
if(is_banned_from(B.ckey, "Cyborg") || QDELETED(src) || QDELETED(B) || QDELETED(user) || QDELETED(M) || !Adjacent(user))
if(!QDELETED(M))
to_chat(user, "<span class='warning'>This [M.name] does not seem to fit!</span>")
return
if(!user.temporarilyRemoveItemFromInventory(W))
return
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot/nocell(get_turf(loc))
if(!O)
return
if(M.laws && M.laws.id != DEFAULT_AI_LAWID)
aisync = 0
lawsync = 0
@@ -298,7 +279,7 @@
if(M.laws.id == DEFAULT_AI_LAWID)
O.make_laws()
SSticker.mode.remove_antag_for_borging(BM.mind)
SSticker.mode.remove_antag_for_borging(B.mind)
O.job = "Cyborg"
O.cell = chest.cell
@@ -309,9 +290,9 @@
qdel(O.mmi)
O.mmi = W //and give the real mmi to the borg.
O.updatename(BM.client)
O.updatename(B.client)
BM.mind.transfer_to(O)
B.mind.transfer_to(O)
if(O.mind && O.mind.special_role)
O.mind.store_memory("As a cyborg, you must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.")
@@ -355,7 +336,6 @@
O.lawupdate = FALSE
O.make_laws()
O.cell = chest.cell
chest.cell.forceMove(O)
chest.cell = null
+4 -4
View File
@@ -58,10 +58,10 @@
user.visible_message("<span class='green'>[user] applies \the [src] on [C]'s [affecting.name].</span>", "<span class='green'>You apply \the [src] on [C]'s [affecting.name].</span>")
var/brute2heal = brute
var/burn2heal = burn
if(user?.mind?.get_skill_speed_modifier(/datum/skill/medical))
var/skillmods = user.mind.get_skill_speed_modifier(/datum/skill/medical)
brute2heal *= (2-skillmods)
burn2heal *= (2-skillmods)
var/skill_mod = user?.mind?.get_skill_modifier(/datum/skill/medical, SKILL_SPEED_MODIFIER)
if(skill_mod)
brute2heal *= (2-skill_mod)
burn2heal *= (2-skill_mod)
if(affecting.heal_damage(brute2heal, burn2heal))
C.update_damage_overlays()
return TRUE
+1 -2
View File
@@ -18,7 +18,6 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/iron=1000)
mats_per_stack = 1000
max_amount = 50
attack_verb = list("hit", "bludgeoned", "whacked")
hitsound = 'sound/weapons/gun/general/grenade_launch.ogg'
@@ -35,7 +34,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
/obj/item/stack/rods/get_main_recipes()
. = ..()
. += GLOB.rod_recipes
/obj/item/stack/rods/update_icon()
var/amount = get_amount()
if((amount <= 5) && (amount > 0))
@@ -182,7 +182,7 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \
singular_name = "reinforced plasma glass sheet"
icon_state = "sheet-prglass"
item_state = "sheet-prglass"
custom_materials = list(/datum/material/plasma=MINERAL_MATERIAL_AMOUNT * 0.5, /datum/material/glass=MINERAL_MATERIAL_AMOUNT, /datum/material/iron = MINERAL_MATERIAL_AMOUNT * 0.5,)
custom_materials = list(/datum/material/plasma=MINERAL_MATERIAL_AMOUNT * 0.5, /datum/material/glass=MINERAL_MATERIAL_AMOUNT, /datum/material/iron = MINERAL_MATERIAL_AMOUNT * 0.5)
armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
resistance_flags = ACID_PROOF
material_flags = MATERIAL_NO_EFFECTS
@@ -247,7 +247,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
. = ..()
. += GLOB.silver_recipes
/*
/*
* Clown
*/
/obj/item/stack/sheet/mineral/bananium
@@ -272,8 +272,8 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \
. = ..()
. += GLOB.bananium_recipes
/*
* Titanium
/*
* Titanium
*/
/obj/item/stack/sheet/mineral/titanium
name = "titanium"
@@ -333,7 +333,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \
/*
* Snow
*/
/obj/item/stack/sheet/mineral/snow
name = "snow"
icon_state = "sheet-snow"
@@ -156,7 +156,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \
desc = "This sheet is an alloy of iron and plasma."
icon_state = "sheet-plasteel"
item_state = "sheet-metal"
custom_materials = list(/datum/material/iron=2000, /datum/material/plasma=2000)
custom_materials = list(/datum/material/iron=MINERAL_MATERIAL_AMOUNT, /datum/material/plasma=MINERAL_MATERIAL_AMOUNT)
throwforce = 10
flags_1 = CONDUCT_1
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80)
@@ -10,7 +10,6 @@
throw_range = 3
attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "smashed")
novariants = FALSE
mats_per_stack = MINERAL_MATERIAL_AMOUNT
var/sheettype = null //this is used for girders in the creation of walls/false walls
var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity.
+10 -7
View File
@@ -24,7 +24,7 @@
var/merge_type = null // This path and its children should merge with this stack, defaults to src.type
var/full_w_class = WEIGHT_CLASS_NORMAL //The weight class the stack should have at amount > 2/3rds max_amount
var/novariants = TRUE //Determines whether the item should update it's sprites based on amount.
var/mats_per_stack = 0
var/list/mats_per_unit //list that tells you how much is in a single unit.
///Datum material type that this stack is made of
var/material_type
//NOTE: When adding grind_results, the amounts should be for an INDIVIDUAL ITEM - these amounts will be multiplied by the stack size in on_grind()
@@ -49,8 +49,11 @@
if(!merge_type)
merge_type = type
if(custom_materials && custom_materials.len)
mats_per_unit = list()
var/in_process_mat_list = custom_materials.Copy()
for(var/i in custom_materials)
custom_materials[getmaterialref(i)] = mats_per_stack * amount
mats_per_unit[getmaterialref(i)] = in_process_mat_list[i]
custom_materials[i] *= amount
. = ..()
if(merge)
for(var/obj/item/stack/S in loc)
@@ -315,8 +318,8 @@
amount -= used
if(check)
zero_amount()
for(var/i in custom_materials)
custom_materials[i] = amount * mats_per_stack
for(var/i in mats_per_unit)
custom_materials[i] = amount * mats_per_unit[i]
update_icon()
update_weight()
return TRUE
@@ -348,9 +351,9 @@
source.add_charge(amount * cost)
else
src.amount += amount
if(custom_materials && custom_materials.len)
for(var/i in custom_materials)
custom_materials[getmaterialref(i)] = MINERAL_MATERIAL_AMOUNT * src.amount
if(mats_per_unit && mats_per_unit.len)
for(var/i in mats_per_unit)
custom_materials[i] = mats_per_unit[i] * src.amount
set_custom_materials() //Refresh
update_icon()
update_weight()
@@ -11,7 +11,6 @@
throw_speed = 3
throw_range = 7
max_amount = 60
mats_per_stack = 500
var/turf_type = null
var/mineralType = null
novariants = TRUE
+1 -1
View File
@@ -56,7 +56,7 @@
ion_trail.start()
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/move_react)
if(full_speed)
user.add_movespeed_modifier(MOVESPEED_ID_JETPACK, priority=100, multiplicative_slowdown=-0.25, movetypes=FLOATING, conflict=MOVE_CONFLICT_JETPACK)
user.add_movespeed_modifier(MOVESPEED_ID_JETPACK, priority=100, multiplicative_slowdown=-0.5, movetypes=FLOATING, conflict=MOVE_CONFLICT_JETPACK)
/obj/item/tank/jetpack/proc/turn_off(mob/user)
on = FALSE
+75
View File
@@ -0,0 +1,75 @@
/obj/machinery/pinpointer_dispenser
name = "wayfinding pinpointer dispenser"
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "ticketmachine"
desc = "Having trouble finding your way? This machine dispenses pinpointers that point to common locations."
density = FALSE
layer = HIGH_OBJ_LAYER
var/list/obj/item/pinpointer/wayfinding/pinpointers = list()
var/spawn_cooldown = 1200 //deciseconds per person to spawn another pinpointer
/obj/machinery/pinpointer_dispenser/attack_hand(mob/living/carbon/user)
if(world.time < pinpointers[user.real_name])
var/secsleft = (pinpointers[user.real_name] - world.time) / 10
to_chat(user, "<span class='warning'>You need to wait [secsleft/60 > 1 ? "[round(secsleft/60)] minute\s" : "[round(secsleft)] second\s"].</span>")
return
to_chat(user, "<span class='notice'>You take a pinpointer from [src].</span>")
var/obj/item/pinpointer/wayfinding/P = new /obj/item/pinpointer/wayfinding(get_turf(src))
user.put_in_hands(P)
P.owner = user.real_name
pinpointers[user.real_name] = world.time + spawn_cooldown
/obj/item/pinpointer/wayfinding //For new players or new stations to help players find their way around
name = "wayfinding pinpointer"
desc = "A handheld tracking device that points to useful places."
icon_state = "pinpointer_crew"
var/owner = null
var/list/beacons = list()
/obj/item/pinpointer/wayfinding/attack_self(mob/living/user)
if(active)
toggle_on()
to_chat(user, "<span class='notice'>You deactivate your pinpointer.</span>")
return
if (!owner)
owner = user.real_name
if(beacons.len)
beacons.Cut()
for(var/obj/machinery/navbeacon/B in GLOB.wayfindingbeacons)
beacons[B.codes["wayfinding"]] = B
if(!beacons.len)
to_chat(user, "<span class='notice'>Your pinpointer fails to detect a signal.</span>")
return
var/A = input(user, "", "Pinpoint") as null|anything in sortList(beacons)
if(!A || QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated())
return
target = beacons[A]
toggle_on()
to_chat(user, "<span class='notice'>You activate your pinpointer.</span>")
/obj/item/pinpointer/wayfinding/examine(mob/user)
. = ..()
var/msg = "Its tracking indicator reads "
if(target)
var/obj/machinery/navbeacon/wayfinding/B = target
msg += "\"[B.codes["wayfinding"]]\"."
else
msg = "Its tracking indicator is blank."
if(owner)
msg += " It belongs to [owner]."
. += msg
/obj/item/pinpointer/wayfinding/scan_for_target()
if(!target) //target can be set to null from above code, or elsewhere
active = FALSE
/obj/machinery/navbeacon/wayfinding
wayfinding = TRUE
+7 -18
View File
@@ -185,26 +185,14 @@
if(istype(P, /obj/item/mmi) && !brain)
var/obj/item/mmi/M = P
if(!M.brainmob)
to_chat(user, "<span class='warning'>Sticking an empty [M.name] into the frame would sort of defeat the purpose!</span>")
return
if(M.brainmob.stat == DEAD)
to_chat(user, "<span class='warning'>Sticking a dead [M.name] into the frame would sort of defeat the purpose!</span>")
if(!M.brain_check(user))
return
if(!M.brainmob.client)
to_chat(user, "<span class='warning'>Sticking an inactive [M.name] into the frame would sort of defeat the purpose.</span>")
return
if(!CONFIG_GET(flag/allow_ai) || (is_banned_from(M.brainmob.ckey, "AI") && !QDELETED(src) && !QDELETED(user) && !QDELETED(M) && !QDELETED(user) && Adjacent(user)))
var/mob/living/brain/B = M.brainmob
if(!CONFIG_GET(flag/allow_ai) || (is_banned_from(B.ckey, "AI") && !QDELETED(src) && !QDELETED(user) && !QDELETED(M) && !QDELETED(user) && Adjacent(user)))
if(!QDELETED(M))
to_chat(user, "<span class='warning'>This [M.name] does not seem to fit!</span>")
return
if(!M.brainmob.mind)
to_chat(user, "<span class='warning'>This [M.name] is mindless!</span>")
return
if(!user.transferItemToLoc(M,src))
return
@@ -234,14 +222,15 @@
P.play_tool_sound(src)
to_chat(user, "<span class='notice'>You connect the monitor.</span>")
if(brain)
SSticker.mode.remove_antag_for_borging(brain.brainmob.mind)
var/mob/living/brain/B = brain.brainmob
SSticker.mode.remove_antag_for_borging(B.mind)
var/mob/living/silicon/ai/A = null
if (brain.overrides_aicore_laws)
A = new /mob/living/silicon/ai(loc, brain.laws, brain.brainmob)
A = new /mob/living/silicon/ai(loc, brain.laws, B)
else
A = new /mob/living/silicon/ai(loc, laws, brain.brainmob)
A = new /mob/living/silicon/ai(loc, laws, B)
if(brain.force_replace_ai_name)
A.fully_replace_character_name(A.name, brain.replacement_ai_name())
@@ -124,7 +124,7 @@
///Material chair
/obj/structure/chair/greyscale
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
item_chair = /obj/item/chair/greyscale
buildstacktype = null //Custom mats handle this
@@ -340,7 +340,7 @@
smash(user)
/obj/item/chair/greyscale
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
origin_type = /obj/structure/chair/greyscale
/obj/item/chair/stool
@@ -24,6 +24,12 @@
if(BURN)
playsound(loc, 'sound/items/welder.ogg', 80, TRUE)
/obj/structure/sign/attack_hand(mob/user)
. = ..()
if(.)
return
user.examinate(src)
/obj/structure/sign/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_WRENCH && buildable_sign)
user.visible_message("<span class='notice'>[user] starts removing [src]...</span>", \
@@ -15,6 +15,11 @@
desc = "A sign labelling an area containing chemical equipment."
icon_state = "chemistry1"
/obj/structure/sign/departments/chemistry/pharmacy
name = "\improper PHARMACY"
desc = "A sign labelling an area containing pharmacy equipment."
icon_state = "pharmacy"
/obj/structure/sign/departments/botany
name = "\improper HYDROPONICS"
desc = "A sign labelling an area as a place where plants are grown."
@@ -93,7 +93,7 @@
/obj/structure/sign/warning/testchamber
name = "\improper TESTING AREA"
desc = "A sign that warns of high power testing equipment in the area. That's either a really powerful laser... or a satellite landing on some person's head."
desc = "A sign that warns of high-power testing equipment in the area. That's either a really powerful laser... or a satellite landing on some person's head."
icon_state = "testchamber"
/obj/structure/sign/warning/firingrange
@@ -113,7 +113,7 @@
/obj/structure/sign/warning/gasmask
name = "\improper CONTAMINATED AIR"
desc = "A sign that warns of dangerous particulates in the air, instructing you to wear a filtration device.."
desc = "A sign that warns of dangerous particulates in the air, instructing you to wear a filtration device."
icon_state = "gasmask"
/obj/structure/sign/warning/chemdiamond
+1 -1
View File
@@ -41,7 +41,7 @@
make_new_table(material.tableVariant)
else
if(material.get_amount() < 1)
to_chat(user, "<span class='warning'>You need one metal sheet to do this!</span>")
to_chat(user, "<span class='warning'>You need one sheet to do this!</span>")
return
to_chat(user, "<span class='notice'>You start adding [material] to [src]...</span>")
if(do_after(user, 20, target = src) && material.use(1))
+1 -1
View File
@@ -205,7 +205,7 @@
/obj/structure/table/greyscale
icon = 'icons/obj/smooth_structures/table_greyscale.dmi'
icon_state = "table"
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
buildstack = null //No buildstack, so generate from mat datums
+2 -2
View File
@@ -139,7 +139,7 @@
contents += secret
/obj/structure/toilet/greyscale
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
buildstacktype = null
/obj/structure/urinal
@@ -398,7 +398,7 @@
/obj/structure/sink/greyscale
icon_state = "sink_greyscale"
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
buildstacktype = null
//Shower Curtains//
@@ -122,7 +122,10 @@
baseturfs = /turf/open/lava/smooth/lava_land_surface
/turf/open/floor/plating/asteroid/lowpressure
initial_gas_mix = OPENTURF_LOW_PRESSURE
baseturfs = /turf/open/floor/plating/asteroid/lowpressure
turf_type = /turf/open/floor/plating/asteroid/lowpressure
/turf/open/floor/plating/asteroid/airless
initial_gas_mix = AIRLESS_ATMOS
@@ -3,6 +3,10 @@
icon_state = "plating"
initial_gas_mix = AIRLESS_ATMOS
/turf/open/floor/plating/lowpressure
initial_gas_mix = OPENTURF_LOW_PRESSURE
baseturfs = /turf/open/floor/plating/lowpressure
/turf/open/floor/plating/abductor
name = "alien floor"
icon_state = "alienpod1"
+2 -2
View File
@@ -151,10 +151,10 @@
else
var/timeleft = SSshuttle.emergency.timeLeft()
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
dat += "ETA: <a href='?_src_=holder;[HrefToken()];edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]</a><BR>"
dat += "ETA: <a href='?_src_=holder;[HrefToken()];edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]</a><BR>"
dat += "<a href='?_src_=holder;[HrefToken()];call_shuttle=2'>Send Back</a><br>"
else
dat += "ETA: <a href='?_src_=holder;[HrefToken()];edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]</a><BR>"
dat += "ETA: <a href='?_src_=holder;[HrefToken()];edit_shuttle_time=1'>[(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]</a><BR>"
dat += "<B>Continuous Round Status</B><BR>"
dat += "<a href='?_src_=holder;[HrefToken()];toggle_continuous=1'>[CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]</a>"
if(CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag])
+1 -1
View File
@@ -444,7 +444,7 @@
var/datum/round_event_control/disease_outbreak/DC = locate(/datum/round_event_control/disease_outbreak) in SSevents.control
E = DC.runEvent()
if("Choose")
var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in sortList(typesof(/datum/disease, /proc/cmp_typepaths_asc))
var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in sortList(typesof(/datum/disease), /proc/cmp_typepaths_asc)
var/datum/round_event_control/disease_outbreak/DC = locate(/datum/round_event_control/disease_outbreak) in SSevents.control
var/datum/round_event/disease_outbreak/DO = DC.runEvent()
DO.virus_type = virus
+6 -8
View File
@@ -6,7 +6,6 @@
return
message_admins("[key_name_admin(usr)] is forcing a random map rotation.")
log_admin("[key_name(usr)] is forcing a random map rotation.")
SSticker.maprotatechecked = 1
SSmapping.maprotate()
/client/proc/adminchangemap()
@@ -36,8 +35,7 @@
var/chosenmap = input("Choose a map to change to", "Change Map") as null|anything in sortList(maprotatechoices)|"Custom"
if (!chosenmap)
return
SSticker.maprotatechecked = 1
if(chosenmap == "Custom")
message_admins("[key_name_admin(usr)] is changing the map to a custom map")
log_admin("[key_name(usr)] is changing the map to a custom map")
@@ -46,29 +44,29 @@
VM.map_name = input("Choose the name for the map", "Map Name") as null|text
if(isnull(VM.map_name))
VM.map_name = "Custom"
var/map_file = input("Pick file:", "Map File") as null|file
if(isnull(map_file))
return
if(copytext("[map_file]",-4) != ".dmm")
to_chat(src, "<span class='warning'>Filename must end in '.dmm': [map_file]</span>")
return
if(!fcopy(map_file, "_maps/custom/[map_file]"))
return
// This is to make sure the map works so the server does not start without a map.
var/datum/parsed_map/M = new (map_file)
if(!M)
to_chat(src, "<span class='warning'>Map '[map_file]' failed to parse properly.</span>")
return
if(!M.bounds)
to_chat(src, "<span class='warning'>Map '[map_file]' has non-existant bounds.</span>")
qdel(M)
return
qdel(M)
var/shuttles = alert("Do you want to modify the shuttles?", "Map Shuttles", "Yes", "No")
@@ -155,6 +155,14 @@
var/give_flash = FALSE
var/give_hud = TRUE
/datum/antagonist/rev/head/on_removal()
if(give_hud)
var/mob/living/carbon/C = owner.current
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = C.getorganslot(ORGAN_SLOT_HUD)
if(S)
S.Remove(C)
return ..()
/datum/antagonist/rev/head/antag_listing_name()
return ..() + "(Leader)"
@@ -227,27 +235,27 @@
. = ..()
/datum/antagonist/rev/head/equip_rev()
var/mob/living/carbon/H = owner.current
if(!ishuman(H) && !ismonkey(H))
var/mob/living/carbon/C = owner.current
if(!ishuman(C) && !ismonkey(C))
return
if(give_flash)
var/obj/item/assembly/flash/T = new(H)
var/obj/item/assembly/flash/T = new(C)
var/list/slots = list (
"backpack" = ITEM_SLOT_BACKPACK,
"left pocket" = ITEM_SLOT_LPOCKET,
"right pocket" = ITEM_SLOT_RPOCKET
)
var/where = H.equip_in_one_of_slots(T, slots)
var/where = C.equip_in_one_of_slots(T, slots)
if (!where)
to_chat(H, "The Syndicate were unfortunately unable to get you a flash.")
to_chat(C, "The Syndicate were unfortunately unable to get you a flash.")
else
to_chat(H, "The flash in your [where] will help you to persuade the crew to join your cause.")
to_chat(C, "The flash in your [where] will help you to persuade the crew to join your cause.")
if(give_hud)
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = new(H)
S.Insert(H, special = FALSE, drop_if_replaced = FALSE)
to_chat(H, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = new()
S.Insert(C)
to_chat(C, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
/datum/team/revolution
name = "Revolution"
+1 -1
View File
@@ -273,7 +273,7 @@
icon_state = "knight_greyscale"
item_state = "knight_greyscale"
armor = list("melee" = 35, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 10, "rad" = 10, "fire" = 40, "acid" = 40)
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR//Can change color and add prefix
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS //Can change color and add prefix
/obj/item/clothing/head/helmet/skull
name = "skull helmet"
+1 -1
View File
@@ -247,7 +247,7 @@
desc = "A classic suit of armour, able to be made from many different materials."
icon_state = "knight_greyscale"
item_state = "knight_greyscale"
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR//Can change color and add prefix
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS//Can change color and add prefix
armor = list("melee" = 35, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 10, "rad" = 10, "fire" = 40, "acid" = 40)
/obj/item/clothing/suit/armor/vest/durathread
+1 -1
View File
@@ -23,7 +23,7 @@
/datum/round_event/disease_outbreak/start()
var/advanced_virus = FALSE
max_severity = 3 + max(FLOOR((world.time - control.earliest_start)/6000, 1),0) //3 symptoms at 20 minutes, plus 1 per 10 minutes
if(prob(20 + (10 * max_severity)))
if(!virus_type && prob(20 + (10 * max_severity)))
advanced_virus = TRUE
if(!virus_type && !advanced_virus)
+1
View File
@@ -36,6 +36,7 @@
else if(status == NOT_ENOUGH_PLAYERS)
message_admins("[role_name] cannot be spawned due to lack of players \
signing up.")
deadchat_broadcast(" did not get enough candidates ([minimum_required]) to spawn.", "<b>[role_name]</b>")
else if(status == SUCCESSFUL_SPAWN)
message_admins("[role_name] spawned successfully.")
if(spawned_mobs.len)
+4
View File
@@ -299,10 +299,14 @@
/datum/plant_gene/trait/glow/shadow
//makes plant emit slightly purple shadows
//adds -potency*(rate*0.2) light power to products
name = "Shadow Emission"
rate = 0.04
glow_color = "#AAD84B"
/datum/plant_gene/trait/glow/shadow/glow_power(obj/item/seeds/S)
return -max(S.potency*(rate*0.2), 0.2)
/datum/plant_gene/trait/glow/white
name = "White Bioluminescence"
glow_color = "#FFFFFF"
+1 -2
View File
@@ -18,7 +18,6 @@
var/refined_type = null //What this ore defaults to being refined into
var/mine_experience = 5 //How much experience do you get for mining this ore?
novariants = TRUE // Ore stacks handle their icon updates themselves to keep the illusion that there's more going
mats_per_stack = MINERAL_MATERIAL_AMOUNT
var/list/stack_overlays
/obj/item/stack/ore/update_icon()
@@ -323,7 +322,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
throwforce = 2
w_class = WEIGHT_CLASS_TINY
custom_materials = list(/datum/material/iron = 400)
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
var/string_attached
var/list/sideslist = list("heads","tails")
var/cooldown = 0
+31 -6
View File
@@ -85,7 +85,6 @@
else
return ..()
/obj/item/mmi/attack_self(mob/user)
if(!brain)
radio.on = !radio.on
@@ -204,17 +203,43 @@
if(brainmob)
var/mob/living/brain/B = brainmob
if(!B.key || !B.mind || B.stat == DEAD)
. += "<span class='warning'>The MMI indicates the brain is completely unresponsive.</span>"
. += "<span class='warning'>\The [src] indicates that the brain is completely unresponsive.</span>"
else if(!B.client)
. += "<span class='warning'>The MMI indicates the brain is currently inactive; it might change.</span>"
. += "<span class='warning'>\The [src] indicates that the brain is currently inactive; it might change.</span>"
else
. += "<span class='notice'>The MMI indicates the brain is active.</span>"
. += "<span class='notice'>\The [src] indicates that the brain is active.</span>"
/obj/item/mmi/relaymove(mob/user)
return //so that the MMI won't get a warning about not being able to move if it tries to move
/obj/item/mmi/proc/brain_check(mob/user)
var/mob/living/brain/B = brainmob
if(!B)
if(user)
to_chat(user, "<span class='warning'>\The [src] indicates that there is no brain present!</span>")
return FALSE
if(!B.key || !B.mind)
if(user)
to_chat(user, "<span class='warning'>\The [src] indicates that their mind is completely unresponsive!</span>")
return FALSE
if(!B.client)
if(user)
to_chat(user, "<span class='warning'>\The [src] indicates that their mind is currently inactive.</span>")
return FALSE
if(B.suiciding || brain?.suicided)
if(user)
to_chat(user, "<span class='warning'>\The [src] indicates that their mind has no will to live!</span>")
return FALSE
if(B.stat == DEAD || brain?.brain_death)
if(user)
to_chat(user, "<span class='warning'>\The [src] indicates that the brain is dead!</span>")
return FALSE
if(brain?.organ_flags & ORGAN_FAILING)
if(user)
to_chat(user, "<span class='warning'>\The [src] indicates that the brain is damaged!</span>")
return FALSE
return TRUE
/obj/item/mmi/syndie
name = "\improper Syndicate Man-Machine Interface"
desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs and AIs created with it."
@@ -121,7 +121,8 @@ Des: Removes all infected images from the alien.
/mob/living/carbon/alien/proc/RemoveInfectionImages()
if (client)
for(var/image/I in client.images)
if(dd_hasprefix_case(I.icon_state, "infected"))
var/searchfor = "infected"
if(findtext(I.icon_state, searchfor, 1, length(searchfor) + 1))
qdel(I)
return
@@ -129,5 +129,6 @@ Des: Removes all images from the mob infected by this embryo
/obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages()
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
for(var/image/I in alien.client.images)
if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner)
var/searchfor = "infected"
if(I.loc == owner && findtext(I.icon_state, searchfor, 1, length(searchfor) + 1))
qdel(I)
@@ -79,6 +79,18 @@
else
C.deconstruct()
visible_message("<span class='warning'>[src] chews through the [C].</span>")
for(var/obj/item/reagent_containers/food/snacks/cheesewedge/cheese in range(1, src))
if(prob(10))
var/cap = CONFIG_GET(number/ratcap)
if(LAZYLEN(SSmobs.cheeserats) >= cap)
visible_message("<span class='warning'>[src] carefully eats the cheese, hiding it from the [cap] mice on the station!</span>")
qdel(cheese)
return
var/mob/living/newmouse = new /mob/living/simple_animal/mouse(loc)
SSmobs.cheeserats += newmouse
visible_message("<span class='notice'>[src] nibbles through the [cheese], attracting another mouse!</span>")
qdel(cheese)
return
/*
* Mouse types
@@ -96,6 +108,10 @@
body_color = "brown"
icon_state = "mouse_brown"
/mob/living/simple_animal/mouse/Destroy()
SSmobs.cheeserats -= src
return ..()
//TOM IS ALIVE! SQUEEEEEEEE~K :)
/mob/living/simple_animal/mouse/brown/Tom
name = "Tom"
-1
View File
@@ -363,7 +363,6 @@ All the important duct code:
icon = 'icons/obj/plumbing/fluid_ducts.dmi'
icon_state = "ducts"
custom_materials = list(/datum/material/iron=500)
mats_per_stack = 500
w_class = WEIGHT_CLASS_TINY
novariants = FALSE
max_amount = 50
+58 -33
View File
@@ -1,20 +1,20 @@
#define FLOODLIGHT_OFF 1
#define FLOODLIGHT_LOW 2
#define FLOODLIGHT_MED 3
#define FLOODLIGHT_HIGH 4
/obj/structure/floodlight_frame
name = "floodlight frame"
desc = "A bare metal frame looking vaguely like a floodlight. Requires wrenching down."
desc = "A bare metal frame looking vaguely like a floodlight. Requires wiring."
max_integrity = 100
icon = 'icons/obj/lighting.dmi'
icon_state = "floodlight_c1"
density = TRUE
var/state = FLOODLIGHT_NEEDS_WRENCHING
var/state = FLOODLIGHT_NEEDS_WIRES
/obj/structure/floodlight_frame/attackby(obj/item/O, mob/user, params)
if(O.tool_behaviour == TOOL_WRENCH && (state == FLOODLIGHT_NEEDS_WRENCHING))
to_chat(user, "<span class='notice'>You secure [src].</span>")
anchored = TRUE
state = FLOODLIGHT_NEEDS_WIRES
desc = "A bare metal frame looking vaguely like a floodlight. Requires wiring."
else if(istype(O, /obj/item/stack/cable_coil) && (state == FLOODLIGHT_NEEDS_WIRES))
if(istype(O, /obj/item/stack/cable_coil) && state == FLOODLIGHT_NEEDS_WIRES)
var/obj/item/stack/S = O
if(S.use(5))
to_chat(user, "<span class='notice'>You wire [src].</span>")
@@ -22,19 +22,36 @@
desc = "A bare metal frame looking vaguely like a floodlight. Requires securing with a screwdriver."
icon_state = "floodlight_c2"
state = FLOODLIGHT_NEEDS_SECURING
else if(istype(O, /obj/item/light/tube) && (state == FLOODLIGHT_NEEDS_LIGHTS))
if(user.transferItemToLoc(O))
to_chat(user, "<span class='notice'>You put lights in [src].</span>")
new /obj/machinery/power/floodlight(src.loc)
qdel(src)
else if(O.tool_behaviour == TOOL_SCREWDRIVER && (state == FLOODLIGHT_NEEDS_SECURING))
return
else
to_chat(user, "You need 5 cables to wire [src].")
return
if(O.tool_behaviour == TOOL_SCREWDRIVER && state == FLOODLIGHT_NEEDS_SECURING)
to_chat(user, "<span class='notice'>You fasten the wiring and electronics in [src].</span>")
name = "secured [name]"
desc = "A bare metal frame that looks like a floodlight. Requires light tubes."
desc = "A bare metal frame that looks like a floodlight. Requires a light tube to complete."
icon_state = "floodlight_c3"
state = FLOODLIGHT_NEEDS_LIGHTS
else
..()
return
if(istype(O, /obj/item/light/tube))
var/obj/item/light/tube/L = O
if(state == FLOODLIGHT_NEEDS_LIGHTS && L.status != 2) //Ready for a light tube, and not broken.
to_chat(user, "<span class='notice'>You put lights in [src].</span>")
new /obj/machinery/power/floodlight(loc)
qdel(src)
qdel(O)
return
else //A minute of silence for all the accidentally broken light tubes.
return
if(istype(O, /obj/item/lightreplacer))
var/obj/item/lightreplacer/L = O
if(state == FLOODLIGHT_NEEDS_LIGHTS && L.CanUse(user))
L.Use(user)
to_chat(user, "<span class='notice'>You put lights in [src].</span>")
new /obj/machinery/power/floodlight(loc)
qdel(src)
return
..()
/obj/machinery/power/floodlight
name = "floodlight"
@@ -46,31 +63,39 @@
integrity_failure = 0.8
idle_power_usage = 100
active_power_usage = 1000
var/list/light_setting_list = list(0, 5, 10, 15)
var/light_power_coefficient = 300
var/setting = 1
anchored = FALSE
light_power = 1.75
var/list/light_setting_list = list(0, 5, 10, 15)
var/light_power_coefficient = 200
var/setting = FLOODLIGHT_OFF
/obj/machinery/power/floodlight/process()
if(avail(active_power_usage))
add_load(active_power_usage)
else
change_setting(1)
var/turf/T = get_turf(src)
var/obj/structure/cable/C = locate() in T
if(!C && powernet)
disconnect_from_network()
if(setting > FLOODLIGHT_OFF) //If on
if(avail(active_power_usage))
add_load(active_power_usage)
else
change_setting(FLOODLIGHT_OFF)
else if(avail(idle_power_usage))
add_load(idle_power_usage)
/obj/machinery/power/floodlight/proc/change_setting(val, mob/user)
if((val < 1) || (val > light_setting_list.len))
/obj/machinery/power/floodlight/proc/change_setting(newval, mob/user)
if((newval < FLOODLIGHT_OFF) || (newval > light_setting_list.len))
return
active_power_usage = light_setting_list[val]
if(!avail(active_power_usage))
return change_setting(val - 1)
setting = val
set_light(light_setting_list[val])
setting = newval
active_power_usage = light_setting_list[setting] * light_power_coefficient
if(!avail(active_power_usage) && setting > FLOODLIGHT_OFF)
return change_setting(setting - 1)
set_light(light_setting_list[setting], light_power)
var/setting_text = ""
if(val > 1)
if(setting > FLOODLIGHT_OFF)
icon_state = "[initial(icon_state)]_on"
else
icon_state = initial(icon_state)
switch(val)
switch(setting)
if(1)
setting_text = "OFF"
if(2)
@@ -376,7 +376,7 @@
/datum/reagent/consumable/nuka_cola/on_mob_metabolize(mob/living/L)
..()
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.75, blacklisted_movetypes=(FLYING|FLOATING))
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.35, blacklisted_movetypes=(FLYING|FLOATING))
/datum/reagent/consumable/nuka_cola/on_mob_end_metabolize(mob/living/L)
L.remove_movespeed_modifier(type)
@@ -556,7 +556,7 @@
/datum/reagent/consumable/monkey_energy/on_mob_metabolize(mob/living/L)
..()
if(ismonkey(L))
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.75, blacklisted_movetypes=(FLYING|FLOATING))
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.35, blacklisted_movetypes=(FLYING|FLOATING))
/datum/reagent/consumable/monkey_energy/on_mob_end_metabolize(mob/living/L)
L.remove_movespeed_modifier(type)
@@ -174,7 +174,7 @@
/datum/reagent/drug/methamphetamine/on_mob_metabolize(mob/living/L)
..()
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-2, blacklisted_movetypes=(FLYING|FLOATING))
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.65, blacklisted_movetypes=(FLYING|FLOATING))
/datum/reagent/drug/methamphetamine/on_mob_end_metabolize(mob/living/L)
L.remove_movespeed_modifier(type)
@@ -422,7 +422,7 @@
/datum/reagent/medicine/ephedrine/on_mob_metabolize(mob/living/L)
..()
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.85, blacklisted_movetypes=(FLYING|FLOATING))
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.5, blacklisted_movetypes=(FLYING|FLOATING))
ADD_TRAIT(L, TRAIT_STUNRESISTANCE, type)
/datum/reagent/medicine/ephedrine/on_mob_end_metabolize(mob/living/L)
@@ -391,9 +391,13 @@
if(MUTCOLORS in N.dna.species.species_traits) //take current alien color and darken it slightly
var/newcolor = ""
var/len = length(N.dna.features["mcolor"])
for(var/i=1, i<=len, i+=1)
var/ascii = text2ascii(N.dna.features["mcolor"],i)
var/string = N.dna.features["mcolor"]
var/len = length(string)
var/char = ""
var/ascii = 0
for(var/i=1, i<=len, i += length(char))
char = string[i]
ascii = text2ascii(char)
switch(ascii)
if(48)
newcolor += "0"
@@ -404,7 +408,7 @@
if(98 to 102)
newcolor += ascii2text(ascii-1) //letters b to f lowercase
if(65)
newcolor +="9"
newcolor += "9"
if(66 to 70)
newcolor += ascii2text(ascii+31) //letters B to F - translates to lowercase
else
@@ -1226,7 +1230,7 @@
/datum/reagent/nitryl/on_mob_metabolize(mob/living/L)
..()
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
L.add_movespeed_modifier(type, update=TRUE, priority=100, multiplicative_slowdown=-0.65, blacklisted_movetypes=(FLYING|FLOATING))
/datum/reagent/nitryl/on_mob_end_metabolize(mob/living/L)
L.remove_movespeed_modifier(type)
@@ -2021,4 +2025,3 @@
reagent_state = SOLID
color = "#E6E6DA"
taste_mult = 0
@@ -209,13 +209,13 @@ Borg Shaker
/datum/reagent/consumable/nothing, /datum/reagent/consumable/orangejuice, /datum/reagent/consumable/peachjuice,
/datum/reagent/consumable/sodawater, /datum/reagent/consumable/space_cola, /datum/reagent/consumable/spacemountainwind,
/datum/reagent/consumable/pwr_game, /datum/reagent/consumable/shamblers, /datum/reagent/consumable/soymilk,
/datum/reagent/consumable/space_up, /datum/reagent/consumable/tea, /datum/reagent/consumable/tomatojuice,
/datum/reagent/consumable/tonic, /datum/reagent/water,
/datum/reagent/consumable/space_up, /datum/reagent/consumable/sugar, /datum/reagent/consumable/tea,
/datum/reagent/consumable/tomatojuice, /datum/reagent/consumable/tonic, /datum/reagent/water,
/datum/reagent/consumable/ethanol/ale, /datum/reagent/consumable/ethanol/applejack, /datum/reagent/consumable/ethanol/beer,
/datum/reagent/consumable/ethanol/cognac, /datum/reagent/consumable/ethanol/creme_de_menthe,
/datum/reagent/consumable/ethanol/champagne, /datum/reagent/consumable/ethanol/cognac, /datum/reagent/consumable/ethanol/creme_de_menthe,
/datum/reagent/consumable/ethanol/creme_de_cacao, /datum/reagent/consumable/ethanol/gin, /datum/reagent/consumable/ethanol/kahlua,
/datum/reagent/consumable/ethanol/rum, /datum/reagent/consumable/ethanol/sake, /datum/reagent/consumable/ethanol/tequila,
/datum/reagent/consumable/ethanol/triple_sec, /datum/reagent/consumable/ethanol/vermouth,
/datum/reagent/consumable/ethanol/triple_sec, /datum/reagent/consumable/ethanol/vermouth, /datum/reagent/consumable/ethanol/vodka,
/datum/reagent/consumable/ethanol/whiskey, /datum/reagent/consumable/ethanol/wine)
/obj/item/reagent_containers/borghypo/borgshaker/attack(mob/M, mob/user)
@@ -210,11 +210,12 @@
if(CLONE)
damage_amt = host_mob.getCloneLoss()
if(damage_amt >= damage.get_value())
if(check_above)
if(check_above)
if(damage_amt >= damage.get_value())
reached_threshold = TRUE
else
if(damage_amt < damage.get_value())
reached_threshold = TRUE
else if(!check_above)
reached_threshold = TRUE
if(reached_threshold)
if(!spent)
@@ -228,7 +229,9 @@
/datum/nanite_program/sensor/damage/make_rule(datum/nanite_program/target)
var/datum/nanite_rule/damage/rule = new(target)
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
rule.above = direction.get_value()
var/datum/nanite_extra_setting/damage_type = extra_settings[NES_DAMAGE_TYPE]
var/datum/nanite_extra_setting/damage = extra_settings[NES_DAMAGE]
rule.above = (direction == "Above")
rule.threshold = damage
rule.damage_type = damage_type
return rule
+5 -4
View File
@@ -133,11 +133,12 @@
if(CLONE)
damage_amt = program.host_mob.getCloneLoss()
if(damage_amt >= threshold)
if(above)
if(above)
if(damage_amt >= threshold)
return TRUE
else
if(damage_amt < threshold)
return TRUE
else if(!above)
return TRUE
return FALSE
/datum/nanite_rule/damage/copy_to(datum/nanite_program/new_program)
@@ -684,7 +684,7 @@ datum/status_effect/stabilized/blue/on_remove()
/datum/status_effect/stabilized/sepia/tick()
if(prob(50) && mod > -1)
mod--
owner.add_movespeed_modifier(MOVESPEED_ID_SEPIA, override = TRUE, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
owner.add_movespeed_modifier(MOVESPEED_ID_SEPIA, override = TRUE, update=TRUE, priority=100, multiplicative_slowdown=-0.5, blacklisted_movetypes=(FLYING|FLOATING))
else if(mod < 1)
mod++
// yeah a value of 0 does nothing but replacing the trait in place is cheaper than removing and adding repeatedly
@@ -900,7 +900,7 @@ datum/status_effect/stabilized/blue/on_remove()
colour = "light pink"
/datum/status_effect/stabilized/lightpink/on_apply()
owner.add_movespeed_modifier(MOVESPEED_ID_SLIME_STATUS, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
owner.add_movespeed_modifier(MOVESPEED_ID_SLIME_STATUS, update=TRUE, priority=100, multiplicative_slowdown=-0.5, blacklisted_movetypes=(FLYING|FLOATING))
return ..()
/datum/status_effect/stabilized/lightpink/tick()
+1
View File
@@ -368,6 +368,7 @@
launch_status = ENDGAME_LAUNCHED
setTimer(SSshuttle.emergencyEscapeTime * engine_coeff)
priority_announce("The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", null, null, "Priority")
SSmapping.mapvote() //If no map vote has been run yet, start one.
if(SHUTTLE_STRANDED)
SSshuttle.checkHostileEnvironment()
+1 -1
View File
@@ -686,7 +686,7 @@
if(timeleft > 1 HOURS)
return "--:--"
else if(timeleft > 0)
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
return "[add_leading(num2text((timeleft / 60) % 60), 2, "0")]:[add_leading(num2text(timeleft % 60), 2, " ")]"
else
return "00:00"
+1 -1
View File
@@ -279,7 +279,7 @@
ADD_TRAIT(H, TRAIT_PIERCEIMMUNE, "dna_vault")
if(VAULT_SPEED)
to_chat(H, "<span class='notice'>Your legs feel faster.</span>")
H.add_movespeed_modifier(MOVESPEED_ID_DNA_VAULT, update=TRUE, priority=100, multiplicative_slowdown=-1, blacklisted_movetypes=(FLYING|FLOATING))
H.add_movespeed_modifier(MOVESPEED_ID_DNA_VAULT, update=TRUE, priority=100, multiplicative_slowdown=-0.4, blacklisted_movetypes=(FLYING|FLOATING))
if(VAULT_QUICK)
to_chat(H, "<span class='notice'>Your arms move as fast as lightning.</span>")
H.next_move_modifier = 0.5
@@ -152,7 +152,7 @@
if(allow_thrust(0.01))
ion_trail.start()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/move_react)
owner.add_movespeed_modifier(MOVESPEED_ID_CYBER_THRUSTER, priority=100, multiplicative_slowdown=-2, movetypes=FLOATING, conflict=MOVE_CONFLICT_JETPACK)
owner.add_movespeed_modifier(MOVESPEED_ID_CYBER_THRUSTER, priority=100, multiplicative_slowdown=-0.5, movetypes=FLOATING, conflict=MOVE_CONFLICT_JETPACK)
if(!silent)
to_chat(owner, "<span class='notice'>You turn your thrusters set on.</span>")
else
+3 -3
View File
@@ -188,17 +188,17 @@
//Cut out the name so it doesn't trigger commands
message = copytext(message, 0, start)+copytext(message, start + length(devilinfo.truename), length(message) + 1)
break
else if(dd_hasprefix(message, L.real_name))
else if(findtext(message, L.real_name, 1, length(L.real_name) + 1))
specific_listeners += L //focus on those with the specified name
//Cut out the name so it doesn't trigger commands
found_string = L.real_name
else if(dd_hasprefix(message, L.first_name()))
else if(findtext(message, L.first_name(), 1, length(L.first_name()) + 1))
specific_listeners += L //focus on those with the specified name
//Cut out the name so it doesn't trigger commands
found_string = L.first_name()
else if(L.mind && L.mind.assigned_role && dd_hasprefix(message, L.mind.assigned_role))
else if(L.mind && L.mind.assigned_role && findtext(message, L.mind.assigned_role, 1, length(L.mind.assigned_role) + 1))
specific_listeners += L //focus on those with the specified job
//Cut out the job so it doesn't trigger commands
found_string = L.mind.assigned_role
+22 -10
View File
@@ -83,27 +83,39 @@
/proc/attempt_cancel_surgery(datum/surgery/S, obj/item/I, mob/living/M, mob/user)
var/selected_zone = user.zone_selected
if(S.status == 1)
M.surgeries -= S
user.visible_message("<span class='notice'>[user] removes [I] from [M]'s [parse_zone(selected_zone)].</span>", \
"<span class='notice'>You remove [I] from [M]'s [parse_zone(selected_zone)].</span>")
qdel(S)
else if(S.can_cancel)
return
if(S.can_cancel)
var/required_tool_type = TOOL_CAUTERY
var/obj/item/close_tool = user.get_inactive_held_item()
var/is_robotic = S.requires_bodypart_type == BODYPART_ROBOTIC
if(is_robotic)
required_tool_type = TOOL_SCREWDRIVER
if(close_tool && close_tool.tool_behaviour == required_tool_type || iscyborg(user))
if (ishuman(M))
var/mob/living/carbon/human/H = M
H.bleed_rate = max( (H.bleed_rate - 3), 0)
M.surgeries -= S
user.visible_message("<span class='notice'>[user] closes [M]'s [parse_zone(selected_zone)] with [close_tool] and removes [I].</span>", \
"<span class='notice'>You close [M]'s [parse_zone(selected_zone)] with [close_tool] and remove [I].</span>")
qdel(S)
else
if(iscyborg(user))
close_tool = locate(/obj/item/cautery) in user.held_items
if(!close_tool)
to_chat(user, "<span class='warning'>You need to equip a cautery in an inactive slot to stop [M]'s surgery!</span>")
return
else if(!close_tool || close_tool.tool_behaviour != required_tool_type)
to_chat(user, "<span class='warning'>You need to hold a [is_robotic ? "screwdriver" : "cautery"] in your inactive hand to stop [M]'s surgery!</span>")
return
if(ishuman(M))
var/mob/living/carbon/human/H = M
H.bleed_rate = max( (H.bleed_rate - 3), 0)
M.surgeries -= S
user.visible_message("<span class='notice'>[user] closes [M]'s [parse_zone(selected_zone)] with [close_tool] and removes [I].</span>", \
"<span class='notice'>You close [M]'s [parse_zone(selected_zone)] with [close_tool] and remove [I].</span>")
qdel(S)
/proc/get_location_modifier(mob/M)
var/turf/T = get_turf(M)
+1 -1
View File
@@ -78,7 +78,7 @@
implement_speed_mod = implements[implement_type] / 100.0
speed_mod /= (get_location_modifier(target) * (1 + surgery.speed_modifier) * implement_speed_mod)
var/modded_time = time * speed_mod * user.mind.get_skill_speed_modifier(/datum/skill/medical)
var/modded_time = time * speed_mod * user.mind.get_skill_modifier(/datum/skill/medical, SKILL_SPEED_MODIFIER)
fail_prob = min(max(0, modded_time - (time * SURGERY_SLOWDOWN_CAP_MULTIPLIER)),99)//if modded_time > time * modifier, then fail_prob = modded_time - time*modifier. starts at 0, caps at 99
+2 -2
View File
@@ -7,7 +7,6 @@
products = list(/obj/item/clothing/glasses/meson/engine = 2,
/obj/item/clothing/glasses/welding = 3,
/obj/item/multitool = 4,
/obj/item/construction/rcd/loaded = 3,
/obj/item/grenade/chem_grenade/smart_metal_foam = 10,
/obj/item/geiger_counter = 5,
/obj/item/stock_parts/cell/high = 10,
@@ -18,7 +17,8 @@
/obj/item/electronics/firelock = 10)
contraband = list(/obj/item/stock_parts/cell/potato = 3)
premium = list(/obj/item/storage/belt/utility = 3,
/obj/item/storage/box/smart_metal_foam = 1)
/obj/item/construction/rcd/loaded = 2,
/obj/item/storage/box/smart_metal_foam = 1)
refill_canister = /obj/item/vending_refill/engivend
default_price = 450
extra_price = 500